From 96e1e8351d2684a27d0042df3103d87fd53c5dda Mon Sep 17 00:00:00 2001 From: Francisco Gil Date: Wed, 5 Mar 2025 16:41:29 +0100 Subject: [PATCH 001/138] installers: escape quotes in assignment of variable Assignment of variable 'force_erase' was done without escaping quotes which led to wrong assignment and the script not forcing the erasure of NAND partitions. While on it, remove it from ccmp1 scripts where its use makes no sense as the 'ubivolscript' script takes care of erasing the partitions. Signed-off-by: Francisco Gil --- .../u-boot-dey/ccimx6ul/install_linux_fw_sd.txt | 16 ++++++++-------- .../u-boot-dey/ccimx6ul/install_linux_fw_usb.txt | 16 ++++++++-------- .../u-boot-dey/ccmp1/install_linux_fw_sd.txt | 14 +++++++------- .../u-boot-dey/ccmp1/install_linux_fw_usb.txt | 14 +++++++------- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_sd.txt index 6beae5bcf..61b3bb892 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_sd.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_sd.txt @@ -171,7 +171,7 @@ setenv bootcmd " if test \"\$\{singlemtdsys\}\" = yes; then run ubivolscript; else - force_erase="-e"; + force_erase=\"-e\"; fi; if test \"\$\{dualboot\}\" = yes; then echo \"\"; @@ -179,7 +179,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; echo \"\"; echo \"\"; - update linux_a mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME} ${force_erase}; + update linux_a mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME} \$\{force_erase\}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_a!\"; echo \"\"; @@ -192,7 +192,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; echo \"\"; echo \"\"; - update linux_b mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME} ${force_erase}; + update linux_b mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME} \$\{force_erase\}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_b!\"; echo \"\"; @@ -205,7 +205,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; echo \"\"; echo \"\"; - update rootfs_a mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME} ${force_erase}; + update rootfs_a mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME} \$\{force_erase\}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_a partition!\"; echo \"\"; @@ -218,7 +218,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; echo \"\"; echo \"\"; - update rootfs_b mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME} ${force_erase}; + update rootfs_b mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME} \$\{force_erase\}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_b partition!\"; echo \"\"; @@ -233,7 +233,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; echo \"\"; echo \"\"; - update linux mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME} ${force_erase}; + update linux mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME} \$\{force_erase\}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux!\"; echo \"\"; @@ -245,7 +245,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; echo \"\"; echo \"\"; - update recovery mmc ${INSTALL_MMCDEV} ${INSTALL_RECOVERY_FILENAME} ${force_erase}; + update recovery mmc ${INSTALL_MMCDEV} ${INSTALL_RECOVERY_FILENAME} \$\{force_erase\}; if test \$? -eq 1; then echo \"[ERROR] Failed to update recovery partition!\"; echo \"\"; @@ -257,7 +257,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; echo \"\"; echo \"\"; - update rootfs mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME} ${force_erase}; + update rootfs mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME} \$\{force_erase\}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs partition!\"; echo \"\"; diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_usb.txt index 3a88ce753..5d8b6e7ce 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_usb.txt @@ -173,7 +173,7 @@ setenv bootcmd " if test \"\$\{singlemtdsys\}\" = yes; then run ubivolscript; else - force_erase="-e"; + force_erase=\"-e\"; fi; usb start; if test \"\$\{dualboot\}\" = yes; then @@ -182,7 +182,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; echo \"\"; echo \"\"; - update linux_a usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME} ${force_erase}; + update linux_a usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME} \$\{force_erase\}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_a!\"; echo \"\"; @@ -195,7 +195,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; echo \"\"; echo \"\"; - update linux_b usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME} ${force_erase}; + update linux_b usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME} \$\{force_erase\}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_b!\"; echo \"\"; @@ -208,7 +208,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; echo \"\"; echo \"\"; - update rootfs_a usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME} ${force_erase}; + update rootfs_a usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME} \$\{force_erase\}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_a partition!\"; echo \"\"; @@ -221,7 +221,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; echo \"\"; echo \"\"; - update rootfs_b usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME} ${force_erase}; + update rootfs_b usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME} \$\{force_erase\}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_b partition!\"; echo \"\"; @@ -236,7 +236,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; echo \"\"; echo \"\"; - update linux usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME} ${force_erase}; + update linux usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME} \$\{force_erase\}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux!\"; echo \"\"; @@ -248,7 +248,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; echo \"\"; echo \"\"; - update recovery usb ${INSTALL_USBDEV} ${INSTALL_RECOVERY_FILENAME} ${force_erase}; + update recovery usb ${INSTALL_USBDEV} ${INSTALL_RECOVERY_FILENAME} \$\{force_erase\}; if test \$? -eq 1; then echo \"[ERROR] Failed to update recovery partition!\"; echo \"\"; @@ -260,7 +260,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; echo \"\"; echo \"\"; - update rootfs usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME} ${force_erase}; + update rootfs usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME} \$\{force_erase\}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs partition!\"; echo \"\"; diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_sd.txt index 0c201f1e4..1b02a2b78 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_sd.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_sd.txt @@ -173,7 +173,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; echo \"\"; echo \"\"; - update linux_a mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME} ${force_erase}; + update linux_a mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_a!\"; echo \"\"; @@ -186,7 +186,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; echo \"\"; echo \"\"; - update linux_b mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME} ${force_erase}; + update linux_b mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_b!\"; echo \"\"; @@ -199,7 +199,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; echo \"\"; echo \"\"; - update rootfs_a mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME} ${force_erase}; + update rootfs_a mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_a partition!\"; echo \"\"; @@ -212,7 +212,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; echo \"\"; echo \"\"; - update rootfs_b mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME} ${force_erase}; + update rootfs_b mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_b partition!\"; echo \"\"; @@ -227,7 +227,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; echo \"\"; echo \"\"; - update linux mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME} ${force_erase}; + update linux mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux!\"; echo \"\"; @@ -239,7 +239,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; echo \"\"; echo \"\"; - update recovery mmc ${INSTALL_MMCDEV} ${INSTALL_RECOVERY_FILENAME} ${force_erase}; + update recovery mmc ${INSTALL_MMCDEV} ${INSTALL_RECOVERY_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update recovery partition!\"; echo \"\"; @@ -251,7 +251,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; echo \"\"; echo \"\"; - update rootfs mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME} ${force_erase}; + update rootfs mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs partition!\"; echo \"\"; diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_usb.txt index eb0432506..73afcbffc 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_usb.txt @@ -176,7 +176,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; echo \"\"; echo \"\"; - update linux_a usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME} ${force_erase}; + update linux_a usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_a!\"; echo \"\"; @@ -189,7 +189,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; echo \"\"; echo \"\"; - update linux_b usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME} ${force_erase}; + update linux_b usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_b!\"; echo \"\"; @@ -202,7 +202,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; echo \"\"; echo \"\"; - update rootfs_a usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME} ${force_erase}; + update rootfs_a usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_a partition!\"; echo \"\"; @@ -215,7 +215,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; echo \"\"; echo \"\"; - update rootfs_b usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME} ${force_erase}; + update rootfs_b usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_b partition!\"; echo \"\"; @@ -230,7 +230,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; echo \"\"; echo \"\"; - update linux usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME} ${force_erase}; + update linux usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux!\"; echo \"\"; @@ -242,7 +242,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; echo \"\"; echo \"\"; - update recovery usb ${INSTALL_USBDEV} ${INSTALL_RECOVERY_FILENAME} ${force_erase}; + update recovery usb ${INSTALL_USBDEV} ${INSTALL_RECOVERY_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update recovery partition!\"; echo \"\"; @@ -254,7 +254,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; echo \"\"; echo \"\"; - update rootfs usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME} ${force_erase}; + update rootfs usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs partition!\"; echo \"\"; From 417941e2749a83c5b393d38696b90aa1bb2e23bd Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Mon, 10 Mar 2025 11:32:45 +0100 Subject: [PATCH 002/138] meta-digi-dey: bump distro version to 5.0-r2 Signed-off-by: Arturo Buzarra --- README.md | 10 +++++++--- meta-digi-dey/conf/distro/dey.conf | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4a073651c..cbce2928c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Digi Embedded Yocto (DEY) 5.0 -## Release 5.0-r1 +## Release 5.0-r2 This document provides information about Digi Embedded Yocto, Digi International's professional embedded Yocto development environment. @@ -79,11 +79,15 @@ Documentation is available online at https://www.digi.com/resources/documentatio # Downloads -* Demo images: https://ftp1.digi.com/support/digiembeddedyocto/5.0/r1/images/ -* Software Development Kit (SDK): https://ftp1.digi.com/support/digiembeddedyocto/5.0/r1/sdk/ +* Demo images: https://ftp1.digi.com/support/digiembeddedyocto/5.0/r2/images/ +* Software Development Kit (SDK): https://ftp1.digi.com/support/digiembeddedyocto/5.0/r2/sdk/ # Release Changelog +## 5.0-r2 + +TODO + ## 5.0-r1 * Release based on [Yocto 5.0 (Scarthgap)](https://www.yoctoproject.org/software-overview/downloads) including: diff --git a/meta-digi-dey/conf/distro/dey.conf b/meta-digi-dey/conf/distro/dey.conf index 8d95571d4..b8aa37b62 100644 --- a/meta-digi-dey/conf/distro/dey.conf +++ b/meta-digi-dey/conf/distro/dey.conf @@ -4,8 +4,8 @@ require include/time64.inc # Discrete version values. YOCTO_MAJOR = "5" YOCTO_MINOR = "0" -DEY_RELEASE = "1" -DEY_BUILD = "3" +DEY_RELEASE = "2" +DEY_BUILD = "1" # Firmware version of the system. DEY_FIRMWARE_VERSION ?= "${YOCTO_MAJOR}.${YOCTO_MINOR}.${DEY_RELEASE}.${DEY_BUILD}" From 1ad2dade8f8a658f57d7485f8c9d1b5347760485 Mon Sep 17 00:00:00 2001 From: Isaac Hermida Date: Wed, 26 Feb 2025 17:16:30 +0100 Subject: [PATCH 003/138] Revert "crank: recipe for crank demos" This reverts commit 93e69dfb3e7511e680a3d42613e205a0c3520a77. https://onedigi.atlassian.net/browse/DEL-9527 Signed-off-by: Isaac Hermida --- meta-digi-arm/recipes-core/udev/udev-extraconf_1.1.bbappend | 6 ------ 1 file changed, 6 deletions(-) diff --git a/meta-digi-arm/recipes-core/udev/udev-extraconf_1.1.bbappend b/meta-digi-arm/recipes-core/udev/udev-extraconf_1.1.bbappend index 090a18c64..58f5fe13b 100644 --- a/meta-digi-arm/recipes-core/udev/udev-extraconf_1.1.bbappend +++ b/meta-digi-arm/recipes-core/udev/udev-extraconf_1.1.bbappend @@ -53,12 +53,6 @@ do_install:append() { >> ${D}${sysconfdir}/udev/rules.d/localextra.rules fi - # Mouse symlink - printf "%s\n%s\n" \ - "# Symlink to the mouse" \ - "SUBSYSTEM==\"input\", KERNEL==\"event[0-9]*\", ENV{ID_INPUT_MOUSE}==\"1\", SYMLINK+=\"input/mouse0\"" \ - >> ${D}${sysconfdir}/udev/rules.d/localextra.rules - install -d ${D}${sysconfdir}/modprobe.d if ${@bb.utils.contains('MACHINE_FEATURES','gpu','false','true',d)}; then # evbug debug tool From 2bda05af8bab5558a2629317d72d762511b4a7b8 Mon Sep 17 00:00:00 2001 From: Isaac Hermida Date: Wed, 26 Feb 2025 17:16:37 +0100 Subject: [PATCH 004/138] Revert "tslib: crank: add 'libts-1.0.so.0' link to the tslib package" This reverts commit 7a06747017368e8b53aace62cb971c510a72b2b0. https://onedigi.atlassian.net/browse/DEL-9527 Signed-off-by: Isaac Hermida --- meta-digi-dey/recipes-graphics/tslib/tslib_1.22.bbappend | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 meta-digi-dey/recipes-graphics/tslib/tslib_1.22.bbappend diff --git a/meta-digi-dey/recipes-graphics/tslib/tslib_1.22.bbappend b/meta-digi-dey/recipes-graphics/tslib/tslib_1.22.bbappend deleted file mode 100644 index e9b7af750..000000000 --- a/meta-digi-dey/recipes-graphics/tslib/tslib_1.22.bbappend +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (C) 2022, Digi International Inc. - -# Crank storyboard engine needs 'libts-1.0.so.0' -do_install:append() { - ln -s libts.so.0 ${D}${libdir}/libts-1.0.so.0 -} - -RPROVIDES:${PN} = "libts-1.0.so.0" From 02cf30583bbd5be41dac5d01cead3c6271efe54d Mon Sep 17 00:00:00 2001 From: Isaac Hermida Date: Wed, 26 Feb 2025 17:20:35 +0100 Subject: [PATCH 005/138] Revert "gcnano-userland: fixes for Crank SB engine" This reverts commit c2da31e0b1e01d51769c2f5ee56315905c4a2692. https://onedigi.atlassian.net/browse/DEL-9527 Signed-off-by: Isaac Hermida --- .../gcnano-userland-multi-binary-stm32mp.bbappend | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-graphics/gcnano-userland/gcnano-userland-multi-binary-stm32mp.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-graphics/gcnano-userland/gcnano-userland-multi-binary-stm32mp.bbappend index a121ebfa7..768461012 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-graphics/gcnano-userland/gcnano-userland-multi-binary-stm32mp.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-graphics/gcnano-userland/gcnano-userland-multi-binary-stm32mp.bbappend @@ -1,17 +1,9 @@ -# Copyright (C) 2022,2023, Digi International Inc. - -# -# Workarounds for Crank storyboard engine -# +# Copyright (C) 2022-2025, Digi International Inc. # Put the devel symlink in the normal package -FILES:libegl-gcnano += "${libdir}/libEGL${SOLIBSDEV}" FILES:libgles1-gcnano += "${libdir}/libGLESv1_CM${SOLIBSDEV}" -FILES:libgles2-gcnano += "${libdir}/libGLESv2${SOLIBSDEV}" FILES:libopenvg-gcnano += "${libdir}/libOpenVG${SOLIBSDEV}" -# Add explicit runtime provides for libEGL.so, libGLESv2.so, libGLESv1_CM.so and libOpenVG.so -RPROVIDES:libegl-gcnano:prepend = "libEGL.so " +# Add explicit runtime provides for libGLESv1_CM.so and libOpenVG.so RPROVIDES:libgles1-gcnano:prepend = "libGLESv1_CM.so " -RPROVIDES:libgles2-gcnano:prepend = "libGLESv2.so " RPROVIDES:libopenvg-gcnano:prepend = "libOpenVG.so " From 503e50f39295c0b0dbf07d85569cef5c2df239bb Mon Sep 17 00:00:00 2001 From: Isaac Hermida Date: Tue, 25 Feb 2025 18:00:30 +0100 Subject: [PATCH 006/138] linux-virtualization: expand lxc config support enable the kernel BTRFS filesystem support for better support with LXC incremental snapshots. Signed-off-by: Isaac Hermida --- meta-digi-arm/recipes-kernel/linux/linux-dey/docker_conf.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/docker_conf.cfg b/meta-digi-arm/recipes-kernel/linux/linux-dey/docker_conf.cfg index 08c1f7a76..bcfdb4c0c 100644 --- a/meta-digi-arm/recipes-kernel/linux/linux-dey/docker_conf.cfg +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/docker_conf.cfg @@ -60,3 +60,5 @@ CONFIG_UNIX_DIAG=m CONFIG_NETFILTER_XT_MATCH_COMMENT=m CONFIG_NETLINK_DIAG=m CONFIG_MACVLAN=y +CONFIG_BTRFS_FS=y +CONFIG_BTRFS_FS_POSIX_ACL=y From fa003b98dff4eb1359b4472c9628b7dfe0fd5645 Mon Sep 17 00:00:00 2001 From: Gabriel Valcazar Date: Wed, 5 Mar 2025 10:58:27 +0100 Subject: [PATCH 007/138] trustfence.bbclass: don't enable partition encryption for ccmp1 platforms This was causing issues when installing an SWU package on ccmp1 platforms with dualboot disabled and Trustfence enabled: the package was triggering an encrypted rootfs update, which isn't supported on these platforms. Signed-off-by: Gabriel Valcazar (cherry picked from commit af06ea16adc85285d9264468d2fd5946ed401de9) --- meta-digi-dey/classes/trustfence.bbclass | 2 ++ 1 file changed, 2 insertions(+) diff --git a/meta-digi-dey/classes/trustfence.bbclass b/meta-digi-dey/classes/trustfence.bbclass index a5d18706e..109339e6b 100644 --- a/meta-digi-dey/classes/trustfence.bbclass +++ b/meta-digi-dey/classes/trustfence.bbclass @@ -28,8 +28,10 @@ TRUSTFENCE_SIGN_FIT_STM:ccmp1 ?= "1" # Partition encryption configuration TRUSTFENCE_ENCRYPT_PARTITIONS ?= "1" TRUSTFENCE_ENCRYPT_PARTITIONS:ccimx9 ?= "0" +TRUSTFENCE_ENCRYPT_PARTITIONS:ccmp1 ?= "0" TRUSTFENCE_ENCRYPT_ROOTFS ?= "${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", "0", "1", d)}" TRUSTFENCE_ENCRYPT_ROOTFS:ccimx9 ?= "0" +TRUSTFENCE_ENCRYPT_ROOTFS:ccmp1 ?= "0" TRUSTFENCE_FILE_BASED_ENCRYPT ?= "${TF_FILE_BASED_ENCRYPT}" # Read-only rootfs From ab097910874f06c738028803ac727b6198e74dda Mon Sep 17 00:00:00 2001 From: Isaac Hermida Date: Wed, 5 Mar 2025 12:00:57 +0100 Subject: [PATCH 008/138] meta-digi-dey: do not include hwlatdetect hwlatdetect is intended for monitoring hw latency on real time systems that rely on SMI interrupts (mainly x86 machines). As our devices that not support it, do not include the tool. Signed-off-by: Isaac Hermida --- .../recipes-core/packagegroups/packagegroup-dey-core.bb | 1 - 1 file changed, 1 deletion(-) diff --git a/meta-digi-dey/recipes-core/packagegroups/packagegroup-dey-core.bb b/meta-digi-dey/recipes-core/packagegroups/packagegroup-dey-core.bb index 65bf3e484..e20b04601 100644 --- a/meta-digi-dey/recipes-core/packagegroups/packagegroup-dey-core.bb +++ b/meta-digi-dey/recipes-core/packagegroups/packagegroup-dey-core.bb @@ -43,7 +43,6 @@ RDEPENDS:${PN} = "\ ${@bb.utils.contains('DISTRO_FEATURES', 'systemd', 'firmwared', '',d)} \ ${@bb.utils.contains("MACHINE_FEATURES", "keyboard", "${VIRTUAL-RUNTIME_keymaps}", "", d)} \ ${@bb.utils.contains("MACHINE_FEATURES", "touchscreen", "${VIRTUAL-RUNTIME_touchscreen}", "",d)} \ - ${@bb.utils.contains('DISTRO_FEATURES', 'rt', 'hwlatdetect', '',d)} \ init-ifupdown \ libdigiapix \ libgpiod \ From ad35c80e1eefc748040dcd910ef75d7f950454c4 Mon Sep 17 00:00:00 2001 From: Isaac Hermida Date: Tue, 4 Mar 2025 11:54:28 +0100 Subject: [PATCH 009/138] linux-dey-6.6: add RT functionality to STM supported platforms This commit integrates the X-LINUX-RT expansion package from the meta-st-x-linux-rt layer, based on the tag "openstlinux-6.6-yocto-scarthgap-mpu-v24.11.06", for Yocto Scarthgap. Currently only the STM CCMP2 is supported in kernel 6.6. https://onedigi.atlassian.net/browse/DEL-9507 Signed-off-by: Isaac Hermida --- .../0010-Rebase-on-v6.6.48-rt40.patch | 15672 ++++++++++++++++ .../0011-v6.6-stm32mp-rt-r1.patch | 386 + .../fragment-08-deactivate-rng.config | 2 + .../recipes-kernel/linux/linux-dey_6.6.bb | 16 + 4 files changed, 16076 insertions(+) create mode 100644 meta-digi-arm/recipes-kernel/linux/linux-dey/stm32mpcommon/0010-Rebase-on-v6.6.48-rt40.patch create mode 100644 meta-digi-arm/recipes-kernel/linux/linux-dey/stm32mpcommon/0011-v6.6-stm32mp-rt-r1.patch create mode 100644 meta-digi-arm/recipes-kernel/linux/linux-dey/stm32mpcommon/fragment-08-deactivate-rng.config diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/stm32mpcommon/0010-Rebase-on-v6.6.48-rt40.patch b/meta-digi-arm/recipes-kernel/linux/linux-dey/stm32mpcommon/0010-Rebase-on-v6.6.48-rt40.patch new file mode 100644 index 000000000..622eac0f4 --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/stm32mpcommon/0010-Rebase-on-v6.6.48-rt40.patch @@ -0,0 +1,15672 @@ +From 853262b793afb5b3b26b0fd0ed6467475328ee2c Mon Sep 17 00:00:00 2001 +From: Lionel VITTE +Date: Tue, 3 Dec 2024 14:40:54 +0100 +Subject: [PATCH 10/11] Rebase on v6.6.48-rt40 + +--- + arch/arm/Kconfig | 6 +- + arch/arm/mm/fault.c | 6 + + arch/arm/vfp/vfpmodule.c | 74 +- + arch/arm64/Kconfig | 1 + + 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 | 92 +- + 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/block/zram/zram_drv.c | 37 + + drivers/block/zram/zram_drv.h | 3 + + .../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/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 +- + fs/proc/consoles.c | 14 +- + include/linux/bottom_half.h | 2 + + include/linux/console.h | 150 ++ + include/linux/entry-common.h | 2 +- + include/linux/entry-kvm.h | 2 +- + include/linux/interrupt.h | 29 + + include/linux/netdevice.h | 4 + + include/linux/printk.h | 30 +- + 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 +- + kernel/Kconfig.preempt | 17 +- + 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 | 8 + + 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/core/dev.c | 39 +- + net/core/skbuff.c | 7 +- + 165 files changed, 5048 insertions(+), 1538 deletions(-) + create mode 100644 kernel/printk/nbcon.c + create mode 100644 localversion-rt + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 57c0448d0..e5a283851 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/mm/fault.c b/arch/arm/mm/fault.c +index fef62e4a9..622a30243 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 7e8773a2d..9fde36fcb 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 9e0c1ac3d..6f7960fad 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/powerpc/Kconfig b/arch/powerpc/Kconfig +index 2fe51e0ad..116444551 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 283c34647..4727f4005 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 2de7f6dcd..739f5b179 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 902611954..2f188137f 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 4ebf2ef28..381c3be3b 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 b1e6d275c..9a8d3970d 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 c785a0200..e05c17078 100644 +--- a/arch/riscv/Kconfig ++++ b/arch/riscv/Kconfig +@@ -48,6 +48,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 +@@ -135,6 +136,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 d0345bd65..23fed53b8 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 d18ce0113..e18710fe5 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 e39a905ac..347cdf56f 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,27 +573,22 @@ 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; + + /* We are already set since the last check */ + 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; +- } ++ return 0; + + /* Make an unaligned destination buffer. */ + dst = (void *)((unsigned long)page_address(page) | 0x1); +@@ -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 riscv_online_cpu(unsigned int cpu) ++{ ++ 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; + } + +-static int check_unaligned_access_boot_cpu(void) ++/* Measure unaligned access on all CPUs present at boot in parallel. */ ++static int check_unaligned_access_all_cpus(void) + { +- check_unaligned_access(0); ++ 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 1b8da4e40..2cb5e6514 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 82d12c93f..7bbf835fe 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 d63b02940..1ff38ebbd 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 831fa4a12..5af3ebec0 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/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c +index 06673c6ca..a5d0f7c06 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 ca7a15bd4..e64eb607e 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/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c +index 172aa10a8..4ae472053 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 d587f807d..5036a3e60 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 d1a25fe6c..5674c3450 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 8a5a038fd..68970d6cf 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 c51badf7b..b6c34198d 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 ce397a879..98c3f5328 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 cfbfbfed3..da2becfbc 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 f5659ebd0..5b6d2f555 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 f2973cd1a..aa77f8601 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 2065be5a1..73d815fc5 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 13fb8e504..b51fb0c97 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 8dc291ff0..5b8d084c9 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 f59081066..014d02029 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 ce1cbee1b..3c51620d0 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 c61066498..48e19e55d 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/tty/serial/21285.c b/drivers/tty/serial/21285.c +index d756fcc88..4de0c975e 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 4a9e71b2d..021949f25 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 9afd5979c..db23b3a02 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 3449f8790..30434718f 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 7fa665017..8b30ca8fd 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 8aed33be2..5367bcc62 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 27430fdd9..17be6ad24 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 6af4e1c12..f522eb502 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 28f9a2679..33699e86e 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 8f472a208..78fc1f17d 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 a3b25779d..53e238c8c 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 a17803da8..2d4e775cd 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 5fab4c978..7090b251d 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 a9c419421..77835ac68 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 b5a7404cb..eabbf8afc 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 362bbcdec..9cd660edb 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 d3cb341f2..364599f25 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 924c1a893..ffd234673 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 ad4ae19b6..1aa5b2b49 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 bcca5627a..5a3f2fc47 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 44c27e5ce..b104c36ce 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 626423022..be4af6eda 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 128b5479e..5004125f3 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 667f52e83..6df7af9ed 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 249cb380c..7fa809a40 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 8bd0f8e45..58c2c460c 100644 +--- a/drivers/tty/serial/fsl_lpuart.c ++++ b/drivers/tty/serial/fsl_lpuart.c +@@ -532,9 +532,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; + } + +@@ -543,7 +543,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); +@@ -553,12 +553,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) +@@ -651,7 +651,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); + +@@ -675,7 +675,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; + } +@@ -703,7 +703,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); +@@ -724,7 +724,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; + } +@@ -879,9 +879,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) +@@ -890,7 +890,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; +@@ -956,9 +956,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) +@@ -968,7 +968,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; +@@ -1170,12 +1170,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; + } + +@@ -1244,7 +1244,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) +@@ -1335,9 +1335,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); + } + } + +@@ -1802,14 +1802,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) +@@ -1859,7 +1859,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); + +@@ -1869,7 +1869,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) +@@ -1932,7 +1932,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); +@@ -1940,7 +1940,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); + } +@@ -1952,7 +1952,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); +@@ -1969,7 +1969,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); + } +@@ -2069,7 +2069,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) +@@ -2124,7 +2124,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, +@@ -2304,7 +2304,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) +@@ -2362,7 +2362,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) +@@ -2480,9 +2480,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); +@@ -2498,7 +2498,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 +@@ -2510,9 +2510,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); +@@ -2528,7 +2528,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); + } + + /* +@@ -3093,7 +3093,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); +@@ -3105,7 +3105,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) { + /* +@@ -3118,7 +3118,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, +@@ -3127,11 +3127,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; +@@ -3141,7 +3141,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 819f957b6..a75eafbcb 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 a5d0df2ba..267c9af4b 100644 +--- a/drivers/tty/serial/imx.c ++++ b/drivers/tty/serial/imx.c +@@ -586,7 +586,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); + +@@ -611,7 +611,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 */ +@@ -782,11 +782,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; + } +@@ -795,9 +795,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; + } + +@@ -911,11 +911,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; + } +@@ -978,7 +978,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); +@@ -1048,7 +1048,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; + } +@@ -1131,7 +1131,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; + +@@ -1140,7 +1140,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); + } + + /* +@@ -1153,9 +1153,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); + } +@@ -1185,9 +1185,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; + } + +@@ -1216,9 +1216,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)) { + +@@ -1476,7 +1476,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); +@@ -1549,7 +1549,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; + } +@@ -1574,21 +1574,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. +@@ -1599,7 +1599,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 | +@@ -1621,7 +1621,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); +@@ -1684,7 +1684,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 +@@ -1812,7 +1812,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) +@@ -1874,7 +1874,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 +@@ -1902,7 +1902,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; + } +@@ -2022,9 +2022,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 +@@ -2052,7 +2052,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); + } + + /* +@@ -2210,10 +2210,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; + } +@@ -2223,10 +2223,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; + } +@@ -2493,9 +2493,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; + } + +@@ -2510,7 +2510,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) +@@ -2518,7 +2518,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); +@@ -2530,7 +2530,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 845ff706b..320b29cd4 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 0c78f6627..2bd640428 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 222afc270..ce0fef7e2 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 d881cdd2a..a25ab1efe 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 b38fe4728..5149a947b 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 99225f1e0..faccd772c 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 aea29b4e6..ee40af20a 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 d2502aaa3..8048fa542 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 9388b9dde..4c1d2089a 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 70a910085..db3b81f2a 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 916507b8f..a252465e7 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 ea5a7911c..2a4c09f3a 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 90953e679..597264b54 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 ea924e9b9..0255646bc 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 135a838f5..1097fca22 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 e99970a94..919f5e5aa 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 cc83b772b..436cc6d52 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 e308d5022..3a95bf5d5 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 29bc80d39..77691fbbf 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 73c60f5ea..46e70e155 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 2e1b1c827..549909644 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 be5c842b5..d824c8318 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 de220ac8c..d46a81cdd 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 ad011f1e2..be7bcd75d 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 5a4d88e13..a82b65155 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 f3cd69346..dbec29d9a 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 7a9924d9b..be9ae5cdd 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 d4ec943cb..6d4006b41 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 ed8798fdf..f0cd976e2 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 7d5aaa8d4..e51ca593a 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 469ad26cd..66fd117d8 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 be08fb6f7..eaa980722 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 f793624fd..2559c9781 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 d195c5de5..b296e57a9 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 f328fa572..f257525f9 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 92b9f6894..a821f5d76 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 48cd2d8d6..96daedd8d 100644 +--- a/drivers/tty/serial/stm32-usart.c ++++ b/drivers/tty/serial/stm32-usart.c +@@ -693,7 +693,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) +@@ -813,9 +813,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) +@@ -1095,7 +1095,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) +@@ -1106,15 +1106,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) +@@ -1203,7 +1203,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. +@@ -1216,7 +1216,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. */ +@@ -1226,7 +1226,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); +@@ -1240,7 +1240,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 */ +@@ -1379,7 +1379,7 @@ static void stm32_usart_set_termios(struct uart_port *port, + + uart_clk = clk_get_rate(stm32_port->clk); + +- spin_lock_irqsave(&port->lock, flags); ++ uart_port_lock_irqsave(port, &flags); + + ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, + isr, +@@ -1599,7 +1599,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)) +@@ -1649,9 +1649,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; + } +@@ -2204,9 +2204,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); +@@ -2220,7 +2220,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) +@@ -2357,7 +2357,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 c671d674b..5bfc0040f 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 3aacd5eb4..4251f4e1b 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 40eeaf835..6aa51a6f8 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 58a4342ad..1e051cc25 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 c8c71c562..d3b5e864b 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 0859394a7..0cc6524f5 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 b225a78f6..404c14aca 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 b06661b80..ed7a6bb55 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 c5d5c2765..78a1c1eea 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 2e5e86a00..9c13dac1d 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 493fc4742..afa52883c 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/fs/proc/consoles.c b/fs/proc/consoles.c +index e0758fe79..270367654 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/linux/bottom_half.h b/include/linux/bottom_half.h +index fc53e0ad5..448bbef47 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/console.h b/include/linux/console.h +index 7de11c763..1eb9580e9 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/entry-common.h b/include/linux/entry-common.h +index d95ab85f9..8b3ab0cc1 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 6813171af..674a622c9 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/interrupt.h b/include/linux/interrupt.h +index 4a1dc88dd..a5091ac97 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/netdevice.h b/include/linux/netdevice.h +index b8e60a204..ffa5248a9 100644 +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -3258,7 +3258,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 e4878bb58..ebebc32e7 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/sched.h b/include/linux/sched.h +index 77f01ac38..c02fd12b4 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 478084f91..719416fe8 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 994c25640..b2b9e6eb9 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 be65de65f..ec46e3b49 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 052df85df..6de3d7aab 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 9ea0b2806..5ded1450a 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 cb8bd759e..ce1fefc37 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/kernel/Kconfig.preempt b/kernel/Kconfig.preempt +index c2f1fd95a..0f3d4c2a4 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/entry/common.c b/kernel/entry/common.c +index 5ff4f1cd3..fd42f0b17 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) +@@ -391,7 +391,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 2e0f75bcb..d952fa5ee 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 ce2889f12..d636a1bbd 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 cba8b1a6a..4c73e0b81 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 1d4bc493b..486c68c11 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 151bd3de5..5c21ba41e 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 21db0df0e..4a10e8c16 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); +@@ -1642,7 +1661,7 @@ static void __sched rt_mutex_handle_deadlock(int res, int detect_deadlock, + WARN(1, "rtmutex deadlock detected\n"); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); +- schedule(); ++ rt_mutex_schedule(); + } + } + +@@ -1737,6 +1756,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 +@@ -1748,6 +1776,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; + } +@@ -1755,7 +1784,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 25ec02394..34a59569d 100644 +--- a/kernel/locking/rwbase_rt.c ++++ b/kernel/locking/rwbase_rt.c +@@ -71,6 +71,7 @@ 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); + + /* +@@ -125,12 +126,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 +241,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 +254,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 +273,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 11ed7ce65..62eac9fd8 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 48a19ed84..38e292454 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 d1473c624..c7196de83 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 ef9f9a4e9..9215df21d 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 f5b388e81..39a2b61c7 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 7d4979d5c..7db6992c5 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 000000000..b941039ee +--- /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 0fca282c0..249576a8c 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 fde338606..e7b808b82 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 18cd25e48..52626d0f1 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 6d10927a0..8d9408d65 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 46612fb15..f893f4cfd 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 11a1fac3a..6f085a159 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 97571d390..1a6f2a90e 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); +@@ -6721,10 +6741,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; + /* +@@ -6750,6 +6774,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) +@@ -6762,16 +6788,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); +@@ -6835,11 +6871,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 +@@ -7035,6 +7067,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) +@@ -8885,6 +8943,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 4c3d0d9f3..63e19b89c 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 b2e1009e5..e7e5569fd 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 f77016823..dd8b35f67 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 5007b25c5..95e1b3df1 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 4ac36eb4c..acd1510e8 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 8cbbbea7f..00cdf0db8 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 21903f524..9f240d9cb 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 bd9716d7b..2fde8af88 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 57e5cb36f..c4ae45701 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 55cbc49f7..1a0ed106b 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 63a8ce717..b3fbe97d1 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 fd398af79..03653fcb3 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 3b7d3e9eb..5a4fefbc0 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 000000000..2af6c89ae +--- /dev/null ++++ b/localversion-rt +@@ -0,0 +1 @@ ++-rt40 +diff --git a/net/core/dev.c b/net/core/dev.c +index 5a5bd339f..8f193d7b8 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -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. +@@ -6682,6 +6673,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; +@@ -11619,7 +11636,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 f0a9ef1ae..682175af4 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -6863,8 +6863,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, +-- +2.25.1 + diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/stm32mpcommon/0011-v6.6-stm32mp-rt-r1.patch b/meta-digi-arm/recipes-kernel/linux/linux-dey/stm32mpcommon/0011-v6.6-stm32mp-rt-r1.patch new file mode 100644 index 000000000..490e43afc --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/stm32mpcommon/0011-v6.6-stm32mp-rt-r1.patch @@ -0,0 +1,386 @@ +From b3f06e85218243adf893ea11fa8010aff110dbe4 Mon Sep 17 00:00:00 2001 +From: Lionel VITTE +Date: Tue, 3 Dec 2024 14:44:47 +0100 +Subject: [PATCH] v6.6-stm32mp-rt-r1 + +--- + .../configs/fragment-07-rt-sysvinit.config | 12 ++++ + arch/arm/configs/fragment-07-rt.config | 32 ++++++++++ + .../configs/fragment-07-rt-sysvinit.config | 12 ++++ + arch/arm64/configs/fragment-07-rt.config | 33 ++++++++++ + drivers/hwtracing/coresight/coresight-cti.h | 10 +++ + drivers/mfd/stm32-lptimer.c | 55 ++++++++++++++++ + drivers/mfd/syscon.c | 3 + + drivers/soc/st/Kconfig | 6 ++ + drivers/soc/st/Makefile | 1 + + drivers/soc/st/stm32_hog.c | 64 +++++++++++++++++++ + drivers/watchdog/stm32_iwdg.c | 2 +- + 11 files changed, 229 insertions(+), 1 deletion(-) + create mode 100644 arch/arm/configs/fragment-07-rt-sysvinit.config + create mode 100644 arch/arm/configs/fragment-07-rt.config + create mode 100644 arch/arm64/configs/fragment-07-rt-sysvinit.config + create mode 100644 arch/arm64/configs/fragment-07-rt.config + create mode 100644 drivers/soc/st/stm32_hog.c + +diff --git a/arch/arm/configs/fragment-07-rt-sysvinit.config b/arch/arm/configs/fragment-07-rt-sysvinit.config +new file mode 100644 +index 000000000..49a4baf60 +--- /dev/null ++++ b/arch/arm/configs/fragment-07-rt-sysvinit.config +@@ -0,0 +1,12 @@ ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_SCHED is not set ++# CONFIG_CGROUP_PIDS is not set ++# CONFIG_CGROUP_RDMA is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_CGROUP_PERF is not set ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++ +diff --git a/arch/arm/configs/fragment-07-rt.config b/arch/arm/configs/fragment-07-rt.config +new file mode 100644 +index 000000000..98bb8735f +--- /dev/null ++++ b/arch/arm/configs/fragment-07-rt.config +@@ -0,0 +1,32 @@ ++CONFIG_PREEMPT_RT=y ++ ++# disable SCHED_MC ++# CONFIG_MCPM is not set ++ ++# Disable CPUFREQ and CPUIDLE ++# CONFIG_CPU_FREQ is not set ++# CONFIG_CPU_IDLE is not set ++ ++# Force to have HIGH_RES_TIMERS ++CONFIG_HIGH_RES_TIMERS=y ++ ++# force do not go to sleep ++# For multiple core, you should set the specific boot options ++# for isolate the core and render it tickless: "isolcpus=2,3 nohz_full=2,3" ++# Warning: to active only if SMP are present ++# CONFIG_HZ_PERIODIC=y ++ ++# to Enable ftrace, you need to enable the following configuraiton: ++# CONFIG_FTRACE=y ++# CONFIG_IRQSOFF_TRACER=y ++# CONFIG_PREEMPT_TRACER=y ++# CONFIG_SCHED_TRACER=y ++# CONFIG_FUNCTION_TRACER=y ++# By default, the ftrace for RT kernel are disabled ++# CONFIG_FTRACE is not set ++# CONFIG_IRQSOFF_TRACER is not set ++# CONFIG_PREEMPT_TRACER is not set ++# CONFIG_SCHED_TRACER is not set ++# CONFIG_FUNCTION_TRACER is not set ++ ++ +diff --git a/arch/arm64/configs/fragment-07-rt-sysvinit.config b/arch/arm64/configs/fragment-07-rt-sysvinit.config +new file mode 100644 +index 000000000..49a4baf60 +--- /dev/null ++++ b/arch/arm64/configs/fragment-07-rt-sysvinit.config +@@ -0,0 +1,12 @@ ++CONFIG_CGROUPS=y ++# CONFIG_CGROUP_SCHED is not set ++# CONFIG_CGROUP_PIDS is not set ++# CONFIG_CGROUP_RDMA is not set ++# CONFIG_CGROUP_FREEZER is not set ++# CONFIG_CGROUP_DEVICE is not set ++# CONFIG_CGROUP_CPUACCT is not set ++# CONFIG_CGROUP_PERF is not set ++# CONFIG_CGROUP_DEBUG is not set ++# CONFIG_CGROUP_NET_PRIO is not set ++# CONFIG_CGROUP_NET_CLASSID is not set ++ +diff --git a/arch/arm64/configs/fragment-07-rt.config b/arch/arm64/configs/fragment-07-rt.config +new file mode 100644 +index 000000000..8451ff348 +--- /dev/null ++++ b/arch/arm64/configs/fragment-07-rt.config +@@ -0,0 +1,33 @@ ++CONFIG_EXPERT=y ++CONFIG_PREEMPT_RT=y ++ ++# disable SCHED_MC ++# CONFIG_MCPM is not set ++ ++# Disable CPUFREQ and CPUIDLE ++# CONFIG_CPU_FREQ is not set ++# CONFIG_CPU_IDLE is not set ++ ++# Force to have HIGH_RES_TIMERS ++CONFIG_HIGH_RES_TIMERS=y ++ ++# force do not go to sleep ++# For multiple core, you should set the specific boot options ++# for isolate the core and render it tickless: "isolcpus=2,3 nohz_full=2,3" ++# Warning: to active only if SMP are present ++# CONFIG_HZ_PERIODIC=y ++ ++# to Enable ftrace, you need to enable the following configuraiton: ++# CONFIG_FTRACE=y ++# CONFIG_IRQSOFF_TRACER=y ++# CONFIG_PREEMPT_TRACER=y ++# CONFIG_SCHED_TRACER=y ++# CONFIG_FUNCTION_TRACER=y ++# By default, the ftrace for RT kernel are disabled ++# CONFIG_FTRACE is not set ++# CONFIG_IRQSOFF_TRACER is not set ++# CONFIG_PREEMPT_TRACER is not set ++# CONFIG_SCHED_TRACER is not set ++# CONFIG_FUNCTION_TRACER is not set ++ ++ +diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h +index cb9ee616d..9f9eaaf29 100644 +--- a/drivers/hwtracing/coresight/coresight-cti.h ++++ b/drivers/hwtracing/coresight/coresight-cti.h +@@ -17,6 +17,16 @@ + + #include "coresight-priv.h" + ++#ifdef CONFIG_PREEMPT_RT ++#undef spin_lock_init ++#define spin_lock_init raw_spin_lock_init ++#define spin_lock raw_spin_lock ++#define spin_unlock raw_spin_unlock ++#undef spin_lock_irqsave ++#define spin_lock_irqsave raw_spin_lock_irqsave ++#define spin_unlock_irqrestore raw_spin_unlock_irqrestore ++#define spinlock_t raw_spinlock_t ++#endif + /* + * Device registers + * 0x000 - 0x144: CTI programming and status +diff --git a/drivers/mfd/stm32-lptimer.c b/drivers/mfd/stm32-lptimer.c +index 9fc94122f..8df3b70f1 100644 +--- a/drivers/mfd/stm32-lptimer.c ++++ b/drivers/mfd/stm32-lptimer.c +@@ -21,6 +21,9 @@ static const struct regmap_config stm32_lptimer_regmap_cfg = { + .reg_stride = sizeof(u32), + .max_register = STM32_LPTIM_MAX_REGISTER, + .fast_io = true, ++#ifdef CONFIG_PREEMPT_RT ++ .use_raw_spinlock = true, ++#endif + }; + + static int stm32_lptimer_detect_encoder(struct stm32_lptimer *ddata) +@@ -85,6 +88,13 @@ static int stm32_lptimer_detect_hwcfgr(struct device *dev, struct stm32_lptimer + return 0; + } + ++#ifdef CONFIG_PREEMPT_RT ++static void stm32_lptimer_clk_disable_unprepare(void *data) ++{ ++ clk_disable_unprepare(data); ++} ++#endif ++ + static int stm32_lptimer_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -100,8 +110,13 @@ static int stm32_lptimer_probe(struct platform_device *pdev) + if (IS_ERR(mmio)) + return PTR_ERR(mmio); + ++#ifdef CONFIG_PREEMPT_RT ++ ddata->regmap = devm_regmap_init_mmio(dev, mmio, ++ &stm32_lptimer_regmap_cfg); ++#else + ddata->regmap = devm_regmap_init_mmio_clk(dev, "mux", mmio, + &stm32_lptimer_regmap_cfg); ++#endif + if (IS_ERR(ddata->regmap)) + return PTR_ERR(ddata->regmap); + +@@ -109,6 +124,16 @@ static int stm32_lptimer_probe(struct platform_device *pdev) + if (IS_ERR(ddata->clk)) + return PTR_ERR(ddata->clk); + ++#ifdef CONFIG_PREEMPT_RT ++ ret = clk_prepare_enable(ddata->clk); ++ if (ret) ++ return ret; ++ ++ ret = devm_add_action_or_reset(dev, stm32_lptimer_clk_disable_unprepare, ddata->clk); ++ if (ret) ++ return ret; ++#endif ++ + ret = stm32_lptimer_detect_hwcfgr(dev, ddata); + if (ret) + return ret; +@@ -122,6 +147,33 @@ static int stm32_lptimer_probe(struct platform_device *pdev) + return devm_of_platform_populate(&pdev->dev); + } + ++#ifdef CONFIG_PREEMPT_RT ++static int stm32_lptimer_suspend(struct device *dev) ++{ ++ struct stm32_lptimer *priv = dev_get_drvdata(dev); ++ ++ clk_disable(priv->clk); ++ ++ return 0; ++} ++ ++static int stm32_lptimer_resume(struct device *dev) ++{ ++ struct stm32_lptimer *priv = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = clk_enable(priv->clk); ++ if (ret) ++ dev_err(dev, "failed to enable clock. Error [%d]\n", ret); ++ ++ return ret; ++} ++ ++static const struct dev_pm_ops stm32_lptim_pm_ops = { ++ SYSTEM_SLEEP_PM_OPS(stm32_lptimer_suspend, stm32_lptimer_resume) ++}; ++#endif ++ + static const struct of_device_id stm32_lptimer_of_match[] = { + { .compatible = "st,stm32-lptimer", }, + { .compatible = "st,stm32mp21-lptimer", }, +@@ -135,6 +187,9 @@ static struct platform_driver stm32_lptimer_driver = { + .driver = { + .name = "stm32-lptimer", + .of_match_table = stm32_lptimer_of_match, ++#ifdef CONFIG_PREEMPT_RT ++ .pm = pm_ptr(&stm32_lptim_pm_ops), ++#endif + }, + }; + module_platform_driver(stm32_lptimer_driver); +diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c +index 7d0e91164..a5746d117 100644 +--- a/drivers/mfd/syscon.c ++++ b/drivers/mfd/syscon.c +@@ -40,6 +40,9 @@ static const struct regmap_config syscon_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, ++#ifdef CONFIG_PREEMPT_RT ++ .use_raw_spinlock = true, ++#endif + }; + + static struct syscon *of_syscon_register(struct device_node *np, bool check_res) +diff --git a/drivers/soc/st/Kconfig b/drivers/soc/st/Kconfig +index 1bde2c16c..704a19692 100644 +--- a/drivers/soc/st/Kconfig ++++ b/drivers/soc/st/Kconfig +@@ -1,5 +1,11 @@ + if ARCH_STM32 + ++config STM32_HOG ++ tristate "STM32 hog" ++ default y ++ help ++ Say y to enable clock(s) at the start of the Kernel. ++ + config STM32_PM_DOMAINS + bool "STM32 PM domains" + depends on MACH_STM32MP157 +diff --git a/drivers/soc/st/Makefile b/drivers/soc/st/Makefile +index 6c71607f6..11f056bfc 100644 +--- a/drivers/soc/st/Makefile ++++ b/drivers/soc/st/Makefile +@@ -1,3 +1,4 @@ ++obj-$(CONFIG_STM32_HOG) += stm32_hog.o + obj-$(CONFIG_STM32_PM_DOMAINS) += stm32_pm_domain.o + obj-$(CONFIG_STM32_RISAB) += stm32_risab.o + obj-$(CONFIG_STM32_RISAF) += stm32_risaf.o +diff --git a/drivers/soc/st/stm32_hog.c b/drivers/soc/st/stm32_hog.c +new file mode 100644 +index 000000000..c8fbf8d5e +--- /dev/null ++++ b/drivers/soc/st/stm32_hog.c +@@ -0,0 +1,64 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (C) 2024, STMicroelectronics - All Rights Reserved ++ */ ++ ++#include ++#include "linux/device.h" ++#include ++#include ++ ++struct stm32_hog_pdata { ++ struct clk_bulk_data *clks; ++ int num_clks; ++}; ++ ++static void stm32_hog_remove(struct platform_device *pdev) ++{ ++ struct stm32_hog_pdata *priv = platform_get_drvdata(pdev); ++ ++ clk_bulk_disable_unprepare(priv->num_clks, priv->clks); ++} ++ ++static int stm32_hog_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct stm32_hog_pdata *priv; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks); ++ if (priv->num_clks < 1) ++ return -ENODEV; ++ ++ ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks); ++ if (ret) { ++ dev_err(dev, "failed to enable bulk clks %d\n", ret); ++ return ret; ++ } ++ ++ platform_set_drvdata(pdev, priv); ++ ++ return 0; ++} ++ ++static const struct of_device_id stm32_hog_match[] = { ++ { .compatible = "st,stm32-hog", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, stm32_hog_match); ++ ++static struct platform_driver stm32_hog_driver = { ++ .probe = stm32_hog_probe, ++ .remove_new = stm32_hog_remove, ++ .driver = { ++ .name = "stm32-hog", ++ .of_match_table = stm32_hog_match, ++ }, ++}; ++module_platform_driver(stm32_hog_driver); ++MODULE_AUTHOR("Gabriel Fernandez "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c +index d7dfab2d3..d6a7b74db 100644 +--- a/drivers/watchdog/stm32_iwdg.c ++++ b/drivers/watchdog/stm32_iwdg.c +@@ -320,7 +320,7 @@ static int stm32_iwdg_irq_init(struct platform_device *pdev, + if (!wdt->data->has_early_wakeup) + return 0; + +- irq = platform_get_irq(pdev, 0); ++ irq = platform_get_irq_optional(pdev, 0); + if (irq <= 0) + return 0; + +-- +2.34.1 + diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/stm32mpcommon/fragment-08-deactivate-rng.config b/meta-digi-arm/recipes-kernel/linux/linux-dey/stm32mpcommon/fragment-08-deactivate-rng.config new file mode 100644 index 000000000..5c36fa79a --- /dev/null +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/stm32mpcommon/fragment-08-deactivate-rng.config @@ -0,0 +1,2 @@ +# Disable RNG to avoid OPTEE Activation +# CONFIG_HW_RANDOM_OPTEE 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 46ac98651..0b994e0f0 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,6 +7,22 @@ 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 \ +" +SRC_URI:append:stm32mpcommon = " \ + ${@bb.utils.contains('DISTRO_FEATURES', 'rt', '${STM_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 \ +" +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 += "btnxpuart" module_conf_btnxpuart = "blacklist btnxpuart" From 51991322e2574def22af60065c92415c451cf672 Mon Sep 17 00:00:00 2001 From: Francisco Gil Date: Thu, 6 Mar 2025 15:50:20 +0100 Subject: [PATCH 010/138] nm: remove dnsmasq as DNS manager dnsmaqs is not able to manage DNS from cellular interface. Now, DNS is managed by systemd-resolved on platforms with systemd and by NetworkManager on sysvinit platforms. https://onedigi.atlassian.net/browse/DEL-9353 Signed-off-by: Francisco Gil --- .../networkmanager/networkmanager/NetworkManager.conf | 4 ++-- .../networkmanager/networkmanager_%.bbappend | 6 +++++- meta-digi-dey/recipes-support/dnsmasq/dnsmasq_%.bbappend | 9 --------- 3 files changed, 7 insertions(+), 12 deletions(-) delete mode 100644 meta-digi-dey/recipes-support/dnsmasq/dnsmasq_%.bbappend diff --git a/meta-digi-dey/recipes-connectivity/networkmanager/networkmanager/NetworkManager.conf b/meta-digi-dey/recipes-connectivity/networkmanager/networkmanager/NetworkManager.conf index e79beed5b..141b14d13 100644 --- a/meta-digi-dey/recipes-connectivity/networkmanager/networkmanager/NetworkManager.conf +++ b/meta-digi-dey/recipes-connectivity/networkmanager/networkmanager/NetworkManager.conf @@ -1,8 +1,8 @@ [main] plugins=ifupdown,keyfile no-auto-default=type:ethernet -dns=dnsmasq -rc-manager=file +dns=##DNS_MANAGER## +rc-manager=##RC_MANAGER## [ifupdown] managed=false diff --git a/meta-digi-dey/recipes-connectivity/networkmanager/networkmanager_%.bbappend b/meta-digi-dey/recipes-connectivity/networkmanager/networkmanager_%.bbappend index 4a4aeb376..12160f06f 100644 --- a/meta-digi-dey/recipes-connectivity/networkmanager/networkmanager_%.bbappend +++ b/meta-digi-dey/recipes-connectivity/networkmanager/networkmanager_%.bbappend @@ -23,7 +23,7 @@ SRC_URI += " \ # Adjust compile time options: # * consolekit depends on X11. Disable to allow building framebuffer images. # -PACKAGECONFIG:remove:dey = "consolekit nss" +PACKAGECONFIG:remove:dey = "consolekit dnsmasq nss" PACKAGECONFIG:append = " gnutls modemmanager ppp concheck" # @@ -43,6 +43,8 @@ WLAN0_STATIC_CIDR = "${@ipaddr_to_cidr('wlan0', d)}" UNMANAGED_DEVICES = "interface-name:p2p*;interface-name:wlan1" UNMANAGED_DEVICES:ccimx9 = "interface-name:p2p-wfd0-0;interface-name:wfd0;interface-name:uap0" +DNS_MANAGER = "${@bb.utils.contains('DISTRO_FEATURES', 'systemd','systemd-resolved','default', d)}" +RC_MANAGER = "${@bb.utils.contains('DISTRO_FEATURES', 'systemd', 'unmanaged', 'file', d)}" inherit update-rc.d @@ -55,6 +57,8 @@ do_install:append() { # Customize NetworkManager # sed -i -e "s,##UNMANAGED_DEVICES##,${UNMANAGED_DEVICES},g" ${D}${sysconfdir}/NetworkManager/NetworkManager.conf + sed -i -e "s,##DNS_MANAGER##,${DNS_MANAGER},g" ${D}${sysconfdir}/NetworkManager/NetworkManager.conf + sed -i -e "s,##RC_MANAGER##,${RC_MANAGER},g" ${D}${sysconfdir}/NetworkManager/NetworkManager.conf # # Connections config files diff --git a/meta-digi-dey/recipes-support/dnsmasq/dnsmasq_%.bbappend b/meta-digi-dey/recipes-support/dnsmasq/dnsmasq_%.bbappend deleted file mode 100644 index be2269f60..000000000 --- a/meta-digi-dey/recipes-support/dnsmasq/dnsmasq_%.bbappend +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (C) 2017, Digi International Inc. - -# Enable DBUS support so it can be used from NetworkManager -PACKAGECONFIG:append = " dbus" - -# NetworkManager will launch 'dnsmasq' using DBUS, so disable the creation -# of runlevel's symlinks and disable its systemd service. -INHIBIT_UPDATERCD_BBCLASS = "1" -SYSTEMD_AUTO_ENABLE = "disable" From 161f6fbe9ae19c50d7e29ad18187a52dc43b20cc Mon Sep 17 00:00:00 2001 From: Gabriel Valcazar Date: Mon, 10 Mar 2025 16:19:23 +0100 Subject: [PATCH 011/138] sdk: build.sh: parametize vigiles config file path with Yocto codename We need separate config files for different DEY versions, so store each file set in a folder named after the corresponding Yocto codename. https://onedigi.atlassian.net/browse/DEL-9539 Signed-off-by: Gabriel Valcazar --- sdk/build.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/build.sh b/sdk/build.sh index 3ebe57cce..715215a35 100755 --- a/sdk/build.sh +++ b/sdk/build.sh @@ -161,6 +161,7 @@ fi [ -z "${DY_USE_CVE_LAYER}" ] && DY_USE_CVE_LAYER="false" [ "${DY_CVE_REPORT}" = "true" ] && [ -z "${DY_VIGILES_DIR}" ] && error "DY_VIGILES_DIR not specified" +[ "${DY_CVE_REPORT}" = "true" ] && [ -z "${DY_CODENAME}" ] && error "DY_CODENAME not specified" # Per-platform data while read -r _pl _tgt; do @@ -290,7 +291,7 @@ for platform in ${DY_PLATFORMS}; do status="non-patched" bbclass="vigiles" [ "${DY_USE_CVE_LAYER}" = "true" ] && { status="patched"; bbclass="digi_ccss"; } - VIGILES_CONF_PATH="${DY_VIGILES_DIR}/configs/${platform}_${status}_config" + VIGILES_CONF_PATH="${DY_VIGILES_DIR}/configs/${DY_CODENAME}/${platform}_${status}_config" # Return error if config file doesn't exist [ ! -f "${VIGILES_CONF_PATH}" ] && error "Cannot find Vigiles config file ${VIGILES_CONF_PATH}" printf "%s" "${VIGILES_CFG}" | sed -e "s,##VIGILES_CONF_PATH##,${VIGILES_CONF_PATH},g" -e "s,##VIGILES_BBCLASS##,${bbclass},g" >> conf/local.conf From a28079cc7e6a35dcad5091a47a49b4e0529f8c39 Mon Sep 17 00:00:00 2001 From: Francisco Gil Date: Tue, 11 Mar 2025 11:35:50 +0100 Subject: [PATCH 012/138] swu: ccmp2: add support for updating U-boot/TFA through SWU packages Refactored some functions to make them more generic, allowing for greater parameter reusability. https://onedigi.atlassian.net/browse/DEL-9526 Signed-off-by: Francisco Gil --- .../classes/dey-swupdate-common.bbclass | 4 +- .../swu-images/files/swupdate_uboot_mmc.sh | 43 +++++++++++++------ 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/meta-digi-dey/classes/dey-swupdate-common.bbclass b/meta-digi-dey/classes/dey-swupdate-common.bbclass index 160702af3..cdcc0af2a 100644 --- a/meta-digi-dey/classes/dey-swupdate-common.bbclass +++ b/meta-digi-dey/classes/dey-swupdate-common.bbclass @@ -116,9 +116,11 @@ SWUPDATE_UBOOT_EXT_TFA ?= ".stm32" SWUPDATE_UBOOT_NAME ?= "${SWUPDATE_UBOOT_PREFIX}-${MACHINE}${SWUPDATE_UBOOT_EXT}" SWUPDATE_UBOOT_NAME:ccimx6sbc ?= "${SWUPDATE_UBOOT_PREFIX}-ccimx6qsbc${SWUPDATE_UBOOT_EXT}" -SWUPDATE_UBOOT_NAME:ccmp1 ?= "${SWUPDATE_UBOOT_PREFIX}-${MACHINE}-optee${FIP_SIGN_SUFFIX}${SWUPDATE_UBOOT_EXT}" +SWUPDATE_UBOOT_NAME:ccmp1 ?= "${SWUPDATE_UBOOT_PREFIX}-${MACHINE}-nand${FIP_SIGN_SUFFIX}${SWUPDATE_UBOOT_EXT}" +SWUPDATE_UBOOT_NAME:ccmp25 ?= "${SWUPDATE_UBOOT_PREFIX}-${MACHINE}-optee-emmc${FIP_SIGN_SUFFIX}${SWUPDATE_UBOOT_EXT}" SWUPDATE_UBOOT_NAME_TFA ?= "" SWUPDATE_UBOOT_NAME_TFA:ccmp1 ?= "${SWUPDATE_UBOOT_PREFIX_TFA}-${MACHINE}-nand${SWUPDATE_UBOOT_EXT_TFA}${TFA_SIGN_SUFFIX}" +SWUPDATE_UBOOT_NAME_TFA:ccmp25 ?= "${SWUPDATE_UBOOT_PREFIX_TFA}-${MACHINE}-optee-emmc${SWUPDATE_UBOOT_EXT_TFA}" SWUPDATE_UBOOT_OFFSET ?= "${BOOTLOADER_SEEK_BOOTPART}" diff --git a/meta-digi-dey/recipes-digi/swu-images/files/swupdate_uboot_mmc.sh b/meta-digi-dey/recipes-digi/swu-images/files/swupdate_uboot_mmc.sh index da772ae94..c4e805f39 100755 --- a/meta-digi-dey/recipes-digi/swu-images/files/swupdate_uboot_mmc.sh +++ b/meta-digi-dey/recipes-digi/swu-images/files/swupdate_uboot_mmc.sh @@ -17,7 +17,9 @@ UBOOT_NAME="$1" UBOOT_ENC="$2" UBOOT_SEEK_KB="$3" UBOOT_REDUNDANT="$4" +UBOOT_TFA_NAME="$5" UBOOT_FILE="/tmp/${UBOOT_NAME}" +UBOOT_TFA_FILE="/tmp/${UBOOT_TFA_NAME}" UBOOT_BLOCK_MAIN="mmcblk0boot0" UBOOT_BLOCK_REDUNDANT="mmcblk0boot1" UBOOT_MMC_DEV_MAIN="/dev/${UBOOT_BLOCK_MAIN}" @@ -235,22 +237,28 @@ append_dek () UBOOT_FILE="${UBOOT_ENCRYPTED_DEK}" } -write_uboot_emmc () +write_artifact_emmc () { - local UBOOT_BLOCK="$1" + local BLOCK_TO_WRITE="$1" + local FILE_TO_WRITE="$2" + local ENABLE_WRITE_ACCESS="$3" - # Enable write access in the U-Boot partition. - echo 0 > "/sys/block/${UBOOT_BLOCK}/force_ro" - # Write the U-Boot into the eMMC. - dd if="${UBOOT_FILE}" of="/dev/${UBOOT_BLOCK}" seek="${UBOOT_SEEK_KB}" bs=1K 2>/dev/null + if [ "${ENABLE_WRITE_ACCESS}" -eq 1 ]; then + # Enable write access in the partition. + echo 0 > "/sys/block/${BLOCK_TO_WRITE}/force_ro" + fi + # Write the file into the eMMC. + dd if="${FILE_TO_WRITE}" of="/dev/${BLOCK_TO_WRITE}" seek="${UBOOT_SEEK_KB}" bs=1K 2>/dev/null local rc=$? - # Disable write access in U-Boot partition. - echo 1 > "/sys/block/${UBOOT_BLOCK}/force_ro" + if [ "${ENABLE_WRITE_ACCESS}" -eq 1 ]; then + # Disable write access in partition. + echo 1 > "/sys/block/${BLOCK_TO_WRITE}/force_ro" + fi # Check update operation result. if [ "${rc}" -ne 0 ]; then - exit_error "## ERROR: failed to write file ${UBOOT_FILE} to /dev/${UBOOT_BLOCK}" "${rc}" + exit_error "## ERROR: failed to write file ${FILE_TO_WRITE} to /dev/${BLOCK_TO_WRITE}" "${rc}" fi - echo "U-Boot successfully writen to /dev/${UBOOT_BLOCK}" + echo "U-Boot successfully writen to /dev/${BLOCK_TO_WRITE}" } # If U-Boot is encrypted, the DEK key blob needs to be extracted from existing U-Boot @@ -258,11 +266,22 @@ write_uboot_emmc () if [ "${UBOOT_ENC}" = "enc" ]; then append_dek fi +# Write TFA. +if expr "${PLATFORM}" : "ccmp2.*" >/dev/null; then + # Write TFA artifact into eMMC BOOT1 and BOOT2 partitions. + write_artifact_emmc ${UBOOT_BLOCK_MAIN} ${UBOOT_TFA_FILE} 1 + if [ "${UBOOT_REDUNDANT}" = "redundant" ]; then + write_artifact_emmc ${UBOOT_BLOCK_REDUNDANT} ${UBOOT_TFA_FILE} 1 + fi + # Redefine block devices to write FIP artifact into 'fip-a' and 'fip-b' partitions. + UBOOT_BLOCK_MAIN="mmcblk0p3" + UBOOT_BLOCK_REDUNDANT="mmcblk0p4" +fi # Write U-Boot -write_uboot_emmc ${UBOOT_BLOCK_MAIN} +write_artifact_emmc ${UBOOT_BLOCK_MAIN} ${UBOOT_FILE} 0 # Check if redundant U-Boot update is requested. if [ "${UBOOT_REDUNDANT}" = "redundant" ]; then - write_uboot_emmc ${UBOOT_BLOCK_REDUNDANT} + write_artifact_emmc ${UBOOT_BLOCK_REDUNDANT} ${UBOOT_FILE} 0 fi # Clean intermediate artifacts. clean_artifacts From 46ba68521bf0c6fdbd171f3fae3661168d14e9af Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Thu, 13 Mar 2025 18:04:26 +0100 Subject: [PATCH 013/138] README: add missing CC93 P/Ns to supported platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add to the list of supported platforms missing CC93 variants: - Dual-core, 1GB, non-wireless - Single-core, 512MB, wireless - Single-core, 512MB, non-wireless Reported-by: Miguel Ángel Perez Signed-off-by: Hector Palacios (cherry picked from commit c357bf97f6499e3b8d7ed2e41b25aa5d760208a3) --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index cbce2928c..7b183ad58 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,9 @@ Software for the following hardware platforms is in production support: ## ConnectCore 93 * ConnectCore 93 System-on-Module (SOM) * [CC-WMX-YC7D-KN](https://www.digi.com/products/models/cc-wmx-yc7d-kn) + * [CC-MX-YC7D-ZN](https://www.digi.com/products/models/cc-mx-yc7d-zn) + * [CC-WMX-ZC6D-L1](https://www.digi.com/products/models/cc-wmx-zc6d-l1) + * [CC-MX-ZC6D-Z1](https://www.digi.com/products/models/cc-mx-zc6d-z1) * ConnectCore 93 Development Kit (DVK) * [CC-WMX93-KIT](https://www.digi.com/products/models/cc-wmx93-kit) ([Get Started](https://www.digi.com/resources/documentation/digidocs/embedded/dey/5.0/cc93/yocto-gs_index)) From c1a4de8bc6ca097b451b6ce4e2699a32aeccccfc Mon Sep 17 00:00:00 2001 From: Isaac Hermida Date: Fri, 7 Mar 2025 12:14:30 +0100 Subject: [PATCH 014/138] meta-digi: move ccimx8m platforms support to kernel 6.6 update the ccimx8m platforms support to kernel 6.6 https://onedigi.atlassian.net/browse/DEL-9536 https://onedigi.atlassian.net/browse/DEL-9537 Signed-off-by: Isaac Hermida --- meta-digi-arm/conf/machine/include/digi-defaults.inc | 4 ++-- meta-digi-arm/recipes-kernel/linux/linux-dey_5.15.bb | 4 ++-- meta-digi-arm/recipes-kernel/linux/linux-dey_6.6.bb | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/meta-digi-arm/conf/machine/include/digi-defaults.inc b/meta-digi-arm/conf/machine/include/digi-defaults.inc index bd7617838..e778d0de5 100644 --- a/meta-digi-arm/conf/machine/include/digi-defaults.inc +++ b/meta-digi-arm/conf/machine/include/digi-defaults.inc @@ -14,8 +14,8 @@ PREFERRED_PROVIDER_virtual/xserver = "xserver-xorg" # ccimx6qp 5.15 2017.03 # ccimx6ul 6.6 2024.04 # ccimx8x 6.6 2024.04 (packaged in imx-boot) -# ccimx8mn 5.15 2020.04 (packaged in imx-boot) -# ccimx8mm 5.15 2020.04 (packaged in imx-boot) +# ccimx8mn 6.6 2020.04 (packaged in imx-boot) +# ccimx8mm 6.6 2020.04 (packaged in imx-boot) # ccimx91 6.6 2024.04 (packaged in imx-boot) # ccimx93 6.6 2024.04 (packaged in imx-boot) # ccmp15 5.15 2021.10 diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey_5.15.bb b/meta-digi-arm/recipes-kernel/linux/linux-dey_5.15.bb index 0a8abdd0a..23710a6c8 100644 --- a/meta-digi-arm/recipes-kernel/linux/linux-dey_5.15.bb +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey_5.15.bb @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2024, Digi International Inc. +# Copyright (C) 2022-2025, Digi International Inc. require recipes-kernel/linux/linux-dey.inc @@ -66,4 +66,4 @@ kernel_do_deploy:append:ccmp1() { fi } -COMPATIBLE_MACHINE = "(ccimx6$|ccimx8m|ccmp1)" +COMPATIBLE_MACHINE = "(ccimx6$|ccmp1)" 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 0b994e0f0..fd9255511 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 @@ -1,4 +1,4 @@ -# Copyright (C) 2024,2025 Digi International Inc. +# Copyright (C) 2024,2025, Digi International Inc. require recipes-kernel/linux/linux-dey.inc @@ -47,4 +47,4 @@ do_install:append:ccmp2() { FILES:${KERNEL_PACKAGE_NAME}-modules:ccmp2 += "${sysconfdir}/modprobe.d" -COMPATIBLE_MACHINE = "(ccimx6ul|ccimx8x|ccimx9|ccmp2)" +COMPATIBLE_MACHINE = "(ccimx6ul|ccimx8m|ccimx8x|ccimx9|ccmp2)" From 710e6550543ff81dafc0ff69d986c94b6bb630d5 Mon Sep 17 00:00:00 2001 From: Isaac Hermida Date: Fri, 7 Mar 2025 12:20:53 +0100 Subject: [PATCH 015/138] u-boot-dey: use version v2024.04 for ccimx8m https://onedigi.atlassian.net/browse/DUB-1095 https://onedigi.atlassian.net/browse/DUB-1096 https://onedigi.atlassian.net/browse/DEL-9537 Signed-off-by: Isaac Hermida --- meta-digi-arm/conf/machine/include/digi-defaults.inc | 4 ++-- .../recipes-bsp/u-boot/u-boot-dey_2020.04.bb | 11 ----------- .../recipes-bsp/u-boot/u-boot-dey_2024.04.bb | 2 +- 3 files changed, 3 insertions(+), 14 deletions(-) delete mode 100644 meta-digi-arm/recipes-bsp/u-boot/u-boot-dey_2020.04.bb diff --git a/meta-digi-arm/conf/machine/include/digi-defaults.inc b/meta-digi-arm/conf/machine/include/digi-defaults.inc index e778d0de5..3085862e5 100644 --- a/meta-digi-arm/conf/machine/include/digi-defaults.inc +++ b/meta-digi-arm/conf/machine/include/digi-defaults.inc @@ -14,8 +14,8 @@ PREFERRED_PROVIDER_virtual/xserver = "xserver-xorg" # ccimx6qp 5.15 2017.03 # ccimx6ul 6.6 2024.04 # ccimx8x 6.6 2024.04 (packaged in imx-boot) -# ccimx8mn 6.6 2020.04 (packaged in imx-boot) -# ccimx8mm 6.6 2020.04 (packaged in imx-boot) +# ccimx8mn 6.6 2024.04 (packaged in imx-boot) +# ccimx8mm 6.6 2024.04 (packaged in imx-boot) # ccimx91 6.6 2024.04 (packaged in imx-boot) # ccimx93 6.6 2024.04 (packaged in imx-boot) # ccmp15 5.15 2021.10 diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey_2020.04.bb b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey_2020.04.bb deleted file mode 100644 index e44aa2f1c..000000000 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey_2020.04.bb +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (C) 2020-2024, Digi International Inc. - -require u-boot-dey.inc -LIC_FILES_CHKSUM = "file://Licenses/README;md5=30503fd321432fc713238f582193b78e" - -DEPENDS += "flex-native bison-native" - -SRCBRANCH = "v2020.04/master" -SRCREV = "${AUTOREV}" - -COMPATIBLE_MACHINE = "(ccimx8m)" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey_2024.04.bb b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey_2024.04.bb index 0a9c70311..b53bb8890 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey_2024.04.bb +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey_2024.04.bb @@ -9,4 +9,4 @@ DEPENDS += "flex-native bison-native" SRCBRANCH = "v2024.04/master" SRCREV = "${AUTOREV}" -COMPATIBLE_MACHINE = "(ccimx6ul|ccimx8x|ccimx9)" +COMPATIBLE_MACHINE = "(ccimx6ul|ccimx8m|ccimx8x|ccimx9)" From 70942f21a0f9cc842d513e02921771311ca46e2c Mon Sep 17 00:00:00 2001 From: Isaac Hermida Date: Mon, 10 Mar 2025 16:05:14 +0100 Subject: [PATCH 016/138] mkproject: add build support for ccimx8m platforms Signed-off-by: Isaac Hermida --- sdk/mkproject.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/mkproject.sh b/sdk/mkproject.sh index 8f684339c..2018054f6 100755 --- a/sdk/mkproject.sh +++ b/sdk/mkproject.sh @@ -28,7 +28,7 @@ MKP_NONE="\033[0m" MKP_CONFIGPATH="${MKP_SCRIPTPATH}/sources/meta-digi/meta-digi-arm/conf/templates" # Blacklist platforms (not officially supported in a DEY release) -MKP_BLACKLIST_PLATFORMS="ccimx6qpsbc ccimx6sbc ccimx8mm-dvk ccimx8mn-dvk ccimx8x-sbc-express ccmp13-dvk ccmp15-dvk" +MKP_BLACKLIST_PLATFORMS="ccimx6qpsbc ccimx6sbc ccimx8x-sbc-express ccmp13-dvk ccmp15-dvk" MKP_SETUP_ENVIRONMENT='#!/bin/bash From 16618e57898b6e876d6c66e731460df158278c32 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Thu, 13 Mar 2025 13:43:59 +0100 Subject: [PATCH 017/138] trustfence-stm-signtools: update recipe and package to binaries v2.19.0 This commit updates the trustfence-stm-signtools package with the latest versions from the STM32 MPU ecosystem v6.0: - STM32MP_KeyGen_CLI v2.19.0 - STM32MP_SigningTool_CLI v2.19.0 These tools are deployed as part of STM32CubeProgrammer v2.19.0, adding support for STM32MP2x processors. https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- ...ols_1.3.bb => nativesdk-trustfence-stm-signtools_1.4.bb} | 0 ...native_1.3.bb => trustfence-stm-signtools-native_1.4.bb} | 0 .../trustfence-stm-signtools/trustfence-stm-signtools.inc | 6 +++--- 3 files changed, 3 insertions(+), 3 deletions(-) rename meta-digi-arm/recipes-bsp/trustfence-stm-signtools/{nativesdk-trustfence-stm-signtools_1.3.bb => nativesdk-trustfence-stm-signtools_1.4.bb} (100%) rename meta-digi-arm/recipes-bsp/trustfence-stm-signtools/{trustfence-stm-signtools-native_1.3.bb => trustfence-stm-signtools-native_1.4.bb} (100%) diff --git a/meta-digi-arm/recipes-bsp/trustfence-stm-signtools/nativesdk-trustfence-stm-signtools_1.3.bb b/meta-digi-arm/recipes-bsp/trustfence-stm-signtools/nativesdk-trustfence-stm-signtools_1.4.bb similarity index 100% rename from meta-digi-arm/recipes-bsp/trustfence-stm-signtools/nativesdk-trustfence-stm-signtools_1.3.bb rename to meta-digi-arm/recipes-bsp/trustfence-stm-signtools/nativesdk-trustfence-stm-signtools_1.4.bb diff --git a/meta-digi-arm/recipes-bsp/trustfence-stm-signtools/trustfence-stm-signtools-native_1.3.bb b/meta-digi-arm/recipes-bsp/trustfence-stm-signtools/trustfence-stm-signtools-native_1.4.bb similarity index 100% rename from meta-digi-arm/recipes-bsp/trustfence-stm-signtools/trustfence-stm-signtools-native_1.3.bb rename to meta-digi-arm/recipes-bsp/trustfence-stm-signtools/trustfence-stm-signtools-native_1.4.bb diff --git a/meta-digi-arm/recipes-bsp/trustfence-stm-signtools/trustfence-stm-signtools.inc b/meta-digi-arm/recipes-bsp/trustfence-stm-signtools/trustfence-stm-signtools.inc index 7dcd1d920..937801845 100644 --- a/meta-digi-arm/recipes-bsp/trustfence-stm-signtools/trustfence-stm-signtools.inc +++ b/meta-digi-arm/recipes-bsp/trustfence-stm-signtools/trustfence-stm-signtools.inc @@ -1,4 +1,4 @@ -# Copyright (C) 2023,2024, Digi International Inc. +# Copyright (C) 2023,2025, Digi International Inc. SUMMARY = "STM key generation and signing tools" SECTION = "console/tools" @@ -10,8 +10,8 @@ PKGNAME = "trustfence-stm-signtools" # tarball SRC_URI = "${DIGI_PKG_SRC}/${PKGNAME}-${PV}.tar.gz" -SRC_URI[md5sum] = "5693ed0cd34f858dea5bc3d1a94e9eee" -SRC_URI[sha256sum] = "be3ab77b98f5f8b8508cda090cc5eeccd0cdc39ebe9cee45b5ef52528c4ce515" +SRC_URI[md5sum] = "a9277220a763b85efac2506dc2ad16f9" +SRC_URI[sha256sum] = "b45ceff90fde83f3e536a84bd7d2ae5ca95d695f35edbdab148f78d3cc31ce5c" inherit bin_package From 76a4f781e359b7e9fc362cab2780ebcab6654749 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Thu, 13 Mar 2025 13:58:59 +0100 Subject: [PATCH 018/138] stm-st-stm32mp: fip-stm32mp: add OP-TEE USB FIP configuration The FIP flavor for OP-TEE + USB is managed in the meta-st-stm32mp layer through the "optee-programmer-usb". However, since we do not require the additional overhead introduced by the STM32CubeProgrammer tool, this commit introduces a new FIP configuration based on OP-TEE for booting from USB. https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- meta-digi-arm/conf/machine/ccmp25-dvk.conf | 1 + meta-digi-arm/conf/machine/include/ccmp2.inc | 2 +- .../recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend | 7 ++++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/meta-digi-arm/conf/machine/ccmp25-dvk.conf b/meta-digi-arm/conf/machine/ccmp25-dvk.conf index b083a1dce..e52935aed 100644 --- a/meta-digi-arm/conf/machine/ccmp25-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp25-dvk.conf @@ -40,6 +40,7 @@ UBOOT_DEVICETREE = "${STM32MP_DEVICETREE}" # Define list of devicetree per supported storage STM32MP_DT_FILES_EMMC = "ccmp25-dvk" STM32MP_DT_FILES_SDCARD = "${STM32MP_DT_FILES_EMMC}" +STM32MP_DT_FILES_USB = "${STM32MP_DT_FILES_EMMC}" # Extra DTB for board - need to specify it with .dtb ... STM32MP_KERNEL_DEVICETREE:ccmp25-dvk += " \ diff --git a/meta-digi-arm/conf/machine/include/ccmp2.inc b/meta-digi-arm/conf/machine/include/ccmp2.inc index aa327ba95..70f4167f4 100644 --- a/meta-digi-arm/conf/machine/include/ccmp2.inc +++ b/meta-digi-arm/conf/machine/include/ccmp2.inc @@ -63,7 +63,7 @@ ROOTFS_DEV_NAME_FINAL = "${@oe.utils.ifelse(d.getVar('TRUSTFENCE_ENCRYPT_ROOTFS' STM32MP_DEVICETREE_PROGRAMMER_ENABLE ?= "0" # List of supported boot devices -BOOTDEVICE_LABELS ?= "emmc" +BOOTDEVICE_LABELS ?= "emmc usb" # Supported boot schemes BOOTSCHEME_LABELS ?= "optee" diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend index db8e28501..8601ff512 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend @@ -1,7 +1,12 @@ # -# Copyright (C) 2024, Digi International Inc. +# Copyright (C) 2024,2025, Digi International Inc. # +# Add optee-usb FIP configuration +STM32MP_DEVICETREE_USB = " ${@' '.join('%s' % dt_file for dt_file in list(dict.fromkeys((d.getVar('STM32MP_DT_FILES_USB') or '').split())))} " +FIP_CONFIG[optee-usb] ?= "optee,${STM32MP_DEVICETREE_USB},default:optee,usb" +FIP_CONFIG += "${@bb.utils.contains('BOOTSCHEME_LABELS', 'optee', bb.utils.contains('BOOTDEVICE_LABELS', 'usb', 'optee-usb', '', d), '', d)}" + # Addons parameters for FIP_WRAPPER FIP_SOC_SEARCH ?= "" FIP_SOC_SEARCH:ccmp2 ?= " stm32mp25 " From b1800736af5ad1ac24c70d6db144f9b176dbca29 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Thu, 13 Mar 2025 16:13:12 +0100 Subject: [PATCH 019/138] trustfence: update support to STM platforms and integrate CCMP2 This commit updates secure boot support based on the STM32 MPU Ecosystem v6.0 and integrates support for the ConnectCore MP2 platform. https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- .../fip-stm32mp/fip-stm32mp.bbappend | 11 ++++ .../tf-a-stm32mp_2.10.bbappend | 53 ++++++------------- .../trustfence-gen-pki-stm.sh | 11 ++-- .../trustfence-sign-artifact-stm.sh | 32 +++++++---- meta-digi-dey/classes/trustfence.bbclass | 31 ++++++----- .../trustfence/trustfence-initramfs.bb | 2 +- 6 files changed, 66 insertions(+), 74 deletions(-) diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend index 8601ff512..6f9ccebd0 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend @@ -7,6 +7,17 @@ STM32MP_DEVICETREE_USB = " ${@' '.join('%s' % dt_file for dt_file in list(dict.f FIP_CONFIG[optee-usb] ?= "optee,${STM32MP_DEVICETREE_USB},default:optee,usb" FIP_CONFIG += "${@bb.utils.contains('BOOTSCHEME_LABELS', 'optee', bb.utils.contains('BOOTDEVICE_LABELS', 'usb', 'optee-usb', '', d), '', d)}" +# Obtain password to use in FIP generation +# Get password from file using the given key index +do_deploy[prefuncs] += "${@oe.utils.conditional('TRUSTFENCE_SIGN', '1', 'set_fip_sign_key', '', d)}" +python set_fip_sign_key() { + passfile = d.getVar('TRUSTFENCE_PASSWORD_FILE') + if (os.path.isfile(passfile)): + with open(passfile, "r") as file: + p = file.read().strip() + if (p): + d.setVar('SIGN_KEY_PASS', p) +} # Addons parameters for FIP_WRAPPER FIP_SOC_SEARCH ?= "" FIP_SOC_SEARCH:ccmp2 ?= " stm32mp25 " diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend index cb1c39ec7..f0d968c09 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend @@ -14,6 +14,13 @@ SRC_URI = " \ ${TFA_GIT_URI};branch=${SRCBRANCH} \ " +# stm32mp15 = header-version 1 +SIGN_TOOL_EXTRA_soc:ccmp15 = " ${@bb.utils.contains('ENCRYPT_ENABLE', '1', '-of ${TF_A_ENCRYPT_OF}', '', d)}" +# stm32mp13 = header-version 2 +SIGN_TOOL_EXTRA_soc:ccmp13 = " ${@bb.utils.contains('ENCRYPT_ENABLE', '1', '-of ${TF_A_ENCRYPT_OF}', '-of ${TF_A_SIGN_OF}', d)}" +# stm32mp2 = header-version 2.2 +SIGN_TOOL_EXTRA_soc:stm32mp2common = " --header-version 2.2 ${@bb.utils.contains('ENCRYPT_ENABLE', '1', '-of ${TF_A_ENCRYPT_OF}', '-of ${TF_A_SIGN_OF}', d)}" + TF_A_CONFIG[nand] = "${DEVICE_BOARD_ENABLE:NAND},STM32MP_RAW_NAND=1 ${@'STM32MP_FORCE_MTD_START_OFFSET=${TF_A_MTD_START_OFFSET_NAND}' if ${TF_A_MTD_START_OFFSET_NAND} else ''} STM32MP_USB_PROGRAMMER=1" # TF_A_CONFIG[uart] (same as 'optee-programmer-uart') TF_A_CONFIG[uart] ?= "\ @@ -43,24 +50,25 @@ do_install[depends] = " \ " # Generate PKI tree if it doesn't exist. -# This is an append to do_compile because in this recipe, the do_deploy -# task comes right after do_compile, and the keys must be ready before that. -do_compile:append() { +# This is an prepend to do_compile because in this recipe, the keys +# must be ready before that. +do_generate_pki_tree() { if ${@oe.utils.conditional('TRUSTFENCE_SIGN','1','true','false',d)}; then check_gen_pki_tree fi } +addtask generate_pki_tree before do_compile after do_configure -# Obtain password to use in FIP generation +# Obtain password to use in TF-A generation # Get password from file using the given key index -do_deploy[prefuncs] += "${@oe.utils.conditional('TRUSTFENCE_SIGN', '1', 'set_fip_sign_key', '', d)}" -python set_fip_sign_key() { +do_compile[prefuncs] += "${@oe.utils.conditional('TRUSTFENCE_SIGN', '1', 'set_tfa_sign_key', '', d)}" +python set_tfa_sign_key() { passfile = d.getVar('TRUSTFENCE_PASSWORD_FILE') if (os.path.isfile(passfile)): with open(passfile, "r") as file: p = file.read().strip() if (p): - d.setVar('FIP_SIGN_KEY_PASS', p) + d.setVar('SIGN_KEY_PASS', p) } # This runs after 'tf_a_sysroot_populate()' which populates all @@ -96,34 +104,3 @@ deploy_symlinks_atf() { fi } SYSROOT_PREPROCESS_FUNCS += "deploy_symlinks_atf" - -# Sign TF-A image -do_deploy[postfuncs] += "${@oe.utils.conditional('TRUSTFENCE_SIGN', '1', 'tfa_sign', '', d)}" -tfa_sign() { - export CONFIG_SIGN_KEYS_PATH="${TRUSTFENCE_SIGN_KEYS_PATH}" - export CONFIG_KEY_INDEX="${TRUSTFENCE_KEY_INDEX}" - - unset i - for config in ${TF_A_CONFIG}; do - i=$(expr $i + 1) - # Initialize devicetree list and tf-a basename - dt_config=$(echo ${TF_A_DEVICETREE} | cut -d',' -f${i}) - tfa_basename=$(echo ${TF_A_BINARIES} | cut -d',' -f${i}) - tfa_file_type=$(echo ${TF_A_FILES} | cut -d',' -f${i}) - for dt in ${dt_config}; do - for file_type in ${tfa_file_type}; do - case "${file_type}" in - bl2) - TF_A_FILENAME="${tfa_basename}-${dt}-${config}.${TF_A_SUFFIX}" - if [ -f "${DEPLOYDIR}/arm-trusted-firmware/${TF_A_FILENAME}" ]; then - trustfence-sign-artifact.sh -p "${DIGI_SOM}" -t "${DEPLOYDIR}/arm-trusted-firmware/${TF_A_FILENAME}" "${DEPLOYDIR}/arm-trusted-firmware/${TF_A_FILENAME}${TFA_SIGN_SUFFIX}" - # the generated artifact lacks 'w' permission which prevents deletion by the build system - chmod u+w "${DEPLOYDIR}/arm-trusted-firmware/${TF_A_FILENAME}${TFA_SIGN_SUFFIX}" - # symlink TF-A - ln -s "arm-trusted-firmware/${TF_A_FILENAME}${TFA_SIGN_SUFFIX}" "${DEPLOYDIR}/" - fi - esac - done # for file_type in ${tfa_file_type} - done # for dt in ${dt_config} - done # for config in ${TF_A_CONFIG} -} diff --git a/meta-digi-arm/recipes-digi/trustfence/trustfence-sign-tools/trustfence-gen-pki-stm.sh b/meta-digi-arm/recipes-digi/trustfence/trustfence-sign-tools/trustfence-gen-pki-stm.sh index 0fcb54aa5..7dcccee17 100755 --- a/meta-digi-arm/recipes-digi/trustfence/trustfence-sign-tools/trustfence-gen-pki-stm.sh +++ b/meta-digi-arm/recipes-digi/trustfence/trustfence-sign-tools/trustfence-gen-pki-stm.sh @@ -3,7 +3,7 @@ # # trustfence-gen-pki-stm.sh # -# Copyright (C) 2023 by Digi International Inc. +# Copyright (C) 2023,2025 by Digi International Inc. # All rights reserved. # # This program is free software; you can redistribute it and/or modify it @@ -24,7 +24,6 @@ while ! mkdir "${SINGLE_PROCESS_LOCK}" > /dev/null 2>&1; do done SCRIPT_NAME="$(basename "${0}")" -SUPPORTED_PLATFORMS="ccmp15, ccmp13" while getopts "p:" c; do case "${c}" in @@ -39,9 +38,8 @@ usage() { Usage: ${SCRIPT_NAME} Options: - -p platform + -p platform (such as ccmp15, ccmp13, ccmp25...) -Supported platforms: ${SUPPORTED_PLATFORMS} EOF } @@ -73,7 +71,7 @@ if [ "${PLATFORM}" = "ccmp15" ]; then echo "${password}" > "${KEY_PASS_FILE}" chmod 400 "${KEY_PASS_FILE}" fi -elif [ "${PLATFORM}" = "ccmp13" ]; then +else if [ "${N_PUBK}" = "8" ] && [ "${N_PRVK}" = "8" ] && [ "${N_PASS}" = "8" ]; then # PKI tree already exists. echo "Using existing PKI tree" @@ -102,7 +100,4 @@ elif [ "${PLATFORM}" = "ccmp13" ]; then echo "[ERROR] Could not generate PKI tree. An incomplete PKI tree may already exist." exit 1 fi -else - echo "Undefined platform" - exit 1 fi diff --git a/meta-digi-arm/recipes-digi/trustfence/trustfence-sign-tools/trustfence-sign-artifact-stm.sh b/meta-digi-arm/recipes-digi/trustfence/trustfence-sign-tools/trustfence-sign-artifact-stm.sh index 52f853c78..4868cb7bd 100755 --- a/meta-digi-arm/recipes-digi/trustfence/trustfence-sign-tools/trustfence-sign-artifact-stm.sh +++ b/meta-digi-arm/recipes-digi/trustfence/trustfence-sign-tools/trustfence-sign-artifact-stm.sh @@ -3,7 +3,7 @@ # # trustfence-sign-artifact.sh # -# Copyright (C) 2023 by Digi International Inc. +# Copyright (C) 2023,2025 by Digi International Inc. # All rights reserved. # # This program is free software; you can redistribute it and/or modify it @@ -26,7 +26,6 @@ while ! mkdir "${SINGLE_PROCESS_LOCK}" > /dev/null 2>&1; do done SCRIPT_NAME="$(basename "${0}")" -SUPPORTED_PLATFORMS="ccmp15, ccmp13" while getopts "p:t" c; do case "${c}" in @@ -42,11 +41,9 @@ usage() { Usage: ${SCRIPT_NAME} [ ] Options: - -p platform + -p platform (such as ccmp15, ccmp13, ccmp25...) -t sign/encrypt TF-A artifact -Supported platforms: ${SUPPORTED_PLATFORMS} - EOF } @@ -72,10 +69,11 @@ if [ "${PLATFORM}" = "ccmp15" ]; then KEY_PASS_FILE="${CONFIG_SIGN_KEYS_PATH}/keys/key_pass.txt" PUBLIC_KEY="${CONFIG_SIGN_KEYS_PATH}/keys/publicKey.pem" PRIVATE_KEY="${CONFIG_SIGN_KEYS_PATH}/keys/privateKey.pem" -elif [ "${PLATFORM}" = "ccmp13" ]; then +else KEY_PASS_FILE="${CONFIG_SIGN_KEYS_PATH}/keys/key_pass0${CONFIG_KEY_INDEX}.txt" PUBLIC_KEY="${CONFIG_SIGN_KEYS_PATH}/keys/publicKey0*.pem" PRIVATE_KEY="${CONFIG_SIGN_KEYS_PATH}/keys/privateKey0${CONFIG_KEY_INDEX}.pem" + TF_A_SIGN_OF="0x00000001" else echo "Undefined platform" exit 1 @@ -95,11 +93,23 @@ PASS=$(cat "${KEY_PASS_FILE}") # Sign TF-A artifact if [ "${ARTIFACT_TFA}" = "y" ]; then - if [ "${PLATFORM}" = "ccmp15" ]; then - SOC_OPTIONS="-hv 1" - elif [ "${PLATFORM}" = "ccmp13" ]; then - SOC_OPTIONS="-hv 2 -of 0x00000001" - fi + case "${PLATFORM}" in + ccmp15) + SOC_OPTIONS="-hv 1" + ;; + ccmp13) + SOC_OPTIONS="-hv 2 -of ${TF_A_SIGN_OF}" + ;; + ccmp2*) + SOC_OPTIONS="-hv 2.2 -of ${TF_A_SIGN_OF}" + ;; + *) + echo "Error: Undefined platform: ${PLATFORM}" + usage + exit 1 + ;; + esac + STM32MP_SigningTool_CLI -bin ${INPUT_FILE} \ --public-key ${PUBLIC_KEY} \ --private-key ${PRIVATE_KEY} \ diff --git a/meta-digi-dey/classes/trustfence.bbclass b/meta-digi-dey/classes/trustfence.bbclass index 109339e6b..b0761d066 100644 --- a/meta-digi-dey/classes/trustfence.bbclass +++ b/meta-digi-dey/classes/trustfence.bbclass @@ -23,15 +23,19 @@ TRUSTFENCE_SRK_REVOKE_MASK ?= "0x0" TRUSTFENCE_KEY_INDEX ?= "0" TRUSTFENCE_SIGN_ARTIFACTS = "1" TRUSTFENCE_SIGN_ARTIFACTS:ccmp1 = "0" +TRUSTFENCE_SIGN_ARTIFACTS:ccmp2 = "0" TRUSTFENCE_SIGN_FIT_STM:ccmp1 ?= "1" +TRUSTFENCE_SIGN_FIT_STM:ccmp2 ?= "1" # Partition encryption configuration TRUSTFENCE_ENCRYPT_PARTITIONS ?= "1" TRUSTFENCE_ENCRYPT_PARTITIONS:ccimx9 ?= "0" TRUSTFENCE_ENCRYPT_PARTITIONS:ccmp1 ?= "0" +TRUSTFENCE_ENCRYPT_PARTITIONS:ccmp2 ?= "0" TRUSTFENCE_ENCRYPT_ROOTFS ?= "${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", "0", "1", d)}" TRUSTFENCE_ENCRYPT_ROOTFS:ccimx9 ?= "0" TRUSTFENCE_ENCRYPT_ROOTFS:ccmp1 ?= "0" +TRUSTFENCE_ENCRYPT_ROOTFS:ccmp2 ?= "0" TRUSTFENCE_FILE_BASED_ENCRYPT ?= "${TF_FILE_BASED_ENCRYPT}" # Read-only rootfs @@ -45,9 +49,11 @@ TRUSTFENCE_READ_ONLY_ROOTFS ?= "${@bb.utils.contains("IMAGE_FEATURES", "read-onl TF_DEK_PATH = "default" TF_DEK_PATH:ccimx9 = "0" TF_DEK_PATH:ccmp1 = "0" +TF_DEK_PATH:ccmp2 = "0" TF_FILE_BASED_ENCRYPT = "0" TF_FILE_BASED_ENCRYPT:ccimx9 = "1" TF_FILE_BASED_ENCRYPT:ccmp1 = "1" +TF_FILE_BASED_ENCRYPT:ccmp2 = "1" # NXP-based sign a FIT-format boot artifact TRUSTFENCE_SIGN_FIT_NXP = "0" @@ -125,11 +131,8 @@ copy_public_key() { elif [ "${DEY_SOC_VENDOR}" = "STM" ]; then if [ "${DIGI_SOM}" = "ccmp15" ]; then PUBLIC_KEY="${TRUSTFENCE_SIGN_KEYS_PATH}/keys/publicKey.pem" - elif [ "${DIGI_SOM}" = "ccmp13" ]; then - PUBLIC_KEY="${TRUSTFENCE_SIGN_KEYS_PATH}/keys/publicKey0${TRUSTFENCE_KEY_INDEX}.pem" else - bberror "Unknown DIGI_SOM" - exit 1 + PUBLIC_KEY="${TRUSTFENCE_SIGN_KEYS_PATH}/keys/publicKey0${TRUSTFENCE_KEY_INDEX}.pem" fi else echo "ERROR: Cannot determine the public key" @@ -171,24 +174,20 @@ python () { if (d.getVar("DEY_SOC_VENDOR") == "NXP"): if (d.getVar("TRUSTFENCE_DEK_PATH") == "default"): d.setVar("TRUSTFENCE_DEK_PATH", d.getVar("TRUSTFENCE_SIGN_KEYS_PATH") + "/dek.bin"); - elif (d.getVar("DEY_SOC_VENDOR") == "STM"): - # Enable authentication capabilities on TF-A independently - # of whether the images are going to be signed by DEY or externally - d.setVar("TF_A_SIGN_ENABLE", "1") - if (d.getVar("TRUSTFENCE_SIGN") == "0"): - d.setVar("FIP_SIGN_ENABLE", "0") if (d.getVar("TRUSTFENCE_SIGN") == "1"): # Set STM-specific variables for signing images if (d.getVar("DEY_SOC_VENDOR") == "STM"): - d.setVar("FIP_SIGN_ENABLE", "1") - d.setVar("FIP_SIGN_KEY_EXTERNAL", "1") + d.setVar("SIGN_ENABLE", "1") + d.setVar("EXTERNAL_KEY_CONF", "1") + d.setVar("SIGN_TOOL", "STM32MP_SigningTool_CLI") if (d.getVar("DIGI_SOM") == "ccmp15" ): - d.setVar("FIP_SIGN_KEY", d.getVar("TRUSTFENCE_SIGN_KEYS_PATH") + "/keys/privateKey.pem"); + d.setVar("SIGN_KEY", d.getVar("TRUSTFENCE_SIGN_KEYS_PATH") + "/keys/privateKey.pem"); d.setVar("TRUSTFENCE_PASSWORD_FILE", d.getVar("TRUSTFENCE_SIGN_KEYS_PATH") + "/keys/key_pass.txt") - elif (d.getVar("DIGI_SOM") == "ccmp13" ): - d.setVar("FIP_SIGN_KEY", d.getVar("TRUSTFENCE_SIGN_KEYS_PATH") + "/keys/privateKey0%s.pem" % d.getVar("TRUSTFENCE_KEY_INDEX")); + else: + d.setVar("SIGN_KEY", d.getVar("TRUSTFENCE_SIGN_KEYS_PATH") + "/keys/privateKey0%s.pem" % d.getVar("TRUSTFENCE_KEY_INDEX")); d.setVar("TRUSTFENCE_PASSWORD_FILE", d.getVar("TRUSTFENCE_SIGN_KEYS_PATH") + "/keys/key_pass0%s.txt" % d.getVar("TRUSTFENCE_KEY_INDEX")) + d.setVar("SIGN_KEY_%s" % (d.getVar("STM32MP_SOC_NAME").strip()), d.getVar("SIGN_KEY")); d.appendVar("UBOOT_TF_CONF", "CONFIG_SIGN_IMAGE=y ") if (d.getVar("TRUSTFENCE_SIGN_ARTIFACTS") == "1"): @@ -265,7 +264,7 @@ python () { # Set the key password. if (d.getVar("DIGI_SOM") == "ccmp15"): d.setVar("SWUPDATE_PASSWORD_FILE", keys_path + "/keys/key_pass.txt") - elif (d.getVar("DIGI_SOM") == "ccmp13"): + else: d.setVar("SWUPDATE_PASSWORD_FILE", keys_path + "/keys/key_pass0" + str(key_index) + ".txt") # Enable partition encryption if rootfs encryption is enabled diff --git a/meta-digi-dey/recipes-core/trustfence/trustfence-initramfs.bb b/meta-digi-dey/recipes-core/trustfence/trustfence-initramfs.bb index 18526acf7..ff1c76cf9 100644 --- a/meta-digi-dey/recipes-core/trustfence/trustfence-initramfs.bb +++ b/meta-digi-dey/recipes-core/trustfence/trustfence-initramfs.bb @@ -26,4 +26,4 @@ RDEPENDS:${PN} = " \ " PACKAGE_ARCH = "${MACHINE_ARCH}" -COMPATIBLE_MACHINE = "(ccimx6|ccimx8m|ccimx8x|ccimx9)" +COMPATIBLE_MACHINE = "(ccimx6|ccimx8m|ccimx8x|ccimx9|ccmp25)" From dc19078620b36d479d4a25f65979b79c01c14491 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Thu, 13 Mar 2025 16:36:25 +0100 Subject: [PATCH 020/138] stm-st-stm32mp: fip-stm32mp: Fix sign key processing This commit corrects an issue in the sign key processing when the SoC name does not match the FIP device tree name. https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- .../recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend index 6f9ccebd0..a183eee65 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend @@ -19,8 +19,8 @@ python set_fip_sign_key() { d.setVar('SIGN_KEY_PASS', p) } # Addons parameters for FIP_WRAPPER -FIP_SOC_SEARCH ?= "" -FIP_SOC_SEARCH:ccmp2 ?= " stm32mp25 " +FIP_SOC_SEARCH ?= " ${STM32MP_SOC_NAME} " +FIP_SOC_MATCH ?= " ${DIGI_SOM} " # Deploy the fip binary for current target do_deploy() { @@ -83,7 +83,9 @@ do_deploy() { unset k for soc in ${STM32MP_SOC_NAME}; do k=$(expr $k + 1) - [ "$(echo ${dt} | grep -c ${soc})" -eq 1 ] && sign_key=$(echo ${SIGN_KEY_PATH_LIST} | cut -d',' -f${k}) + if [ "$(echo ${dt} | grep -c ${soc})" -eq 1 ] || [ "$(echo ${dt} | grep -c ${FIP_SOC_MATCH})" -eq 1 ] ;then + sign_key=$(echo ${SIGN_KEY_PATH_LIST} | cut -d',' -f${k}) + fi done fi FIP_PARAM_SIGN="--sign --signature-key $sign_key --signature-key-pass $sign_single_key_pass" From b5e94fc1ae9f0428ca5edc00970cd835b15a2286 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Thu, 13 Mar 2025 17:28:18 +0100 Subject: [PATCH 021/138] stm-st-stm32mp: tf-a-stm32mp: Add custom do_compile() to fix TF-A artifact signing This commit introduces a custom do_compile() function to resolve a signing issue affecting the final TF-A artifact, where the SoC name does not match the TF-A device tree name. https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- .../tf-a-stm32mp_2.10.bbappend | 225 ++++++++++++++++++ 1 file changed, 225 insertions(+) diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend index f0d968c09..d84e689f6 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend @@ -71,6 +71,231 @@ python set_tfa_sign_key() { d.setVar('SIGN_KEY_PASS', p) } +TF_A_SOC_MATCH ?= " ${DIGI_SOM} " + +do_compile() { + unset LDFLAGS + unset CFLAGS + unset CPPFLAGS + + unset i + for config in ${TF_A_CONFIG}; do + i=$(expr $i + 1) + # Initialize devicetree list, extra make options and tf-a basename + dt_config=$(echo ${TF_A_DEVICETREE} | cut -d',' -f${i}) + extra_opt=$(echo ${TF_A_EXTRA_OPTFLAGS} | cut -d',' -f${i}) + tfa_basename=$(echo ${TF_A_BINARIES} | cut -d',' -f${i}) + tf_a_make_target=$(echo ${TF_A_MAKE_TARGET} | cut -d',' -f${i}) + for dt in ${dt_config}; do + # Init specific soc settings + soc_extra_opt="" + soc_suffix="" + soc_name="" + if [ -n "${STM32MP_SOC_NAME}" ]; then + for soc in ${STM32MP_SOC_NAME}; do + if [ "$(echo ${dt} | grep -c ${soc})" -eq 1 ]; then + soc_extra_opt="$(echo ${soc} | awk '{print toupper($0)}')=1" + soc_suffix="-${soc}" + + SIGN_TOOL_EXTRA_soc="${SIGN_TOOL_EXTRA}" + case ${soc} in + stm32mp13) + SIGN_TOOL_EXTRA_soc="${SIGN_TOOL_EXTRA_stm32mp13}" + ;; + stm32mp15) + SIGN_TOOL_EXTRA_soc="${SIGN_TOOL_EXTRA_stm32mp15}" + if echo ${config} | grep -q 'optee-'; then + soc_extra_opt="${soc_extra_opt} STM32MP1_OPTEE_IN_SYSRAM=1" + fi + ;; + stm32mp21) + SIGN_TOOL_EXTRA_soc="${SIGN_TOOL_EXTRA_stm32mp21}" + ;; + stm32mp23) + SIGN_TOOL_EXTRA_soc="${SIGN_TOOL_EXTRA_stm32mp23}" + ;; + stm32mp25) + SIGN_TOOL_EXTRA_soc="${SIGN_TOOL_EXTRA_stm32mp25}" + ;; + esac + fi + done + fi + mkdir -p ${B}/${config}${soc_suffix}-${dt} + if [ "${TF_A_ENABLE_METADATA}" = "1" ]; then + rm -rf "${B}/${config}${soc_suffix}-${dt}/${TF_A_METADATA_NAME}.${TF_A_METADATA_SUFFIX}" + ${TF_A_METADATA_TOOL} ${TF_A_METADATA_TOOL_ARGS} "${B}/${TF_A_METADATA_NAME}.${TF_A_METADATA_SUFFIX}" + fi + + # generate dt to check the content + oe_runmake -C "${S}" BUILD_PLAT="${B}/${config}${soc_suffix}-${dt}" DTB_FILE_NAME="${dt}.dtb" ${extra_opt} ${soc_extra_opt} dtbs + + # check which pmic1l is present on dtb + pcmi1_present=$(${STAGING_BINDIR_NATIVE}/fdtdump ${B}/${config}${soc_suffix}-${dt}/fdts/${dt}-bl2.dtb 2>/dev/null | grep -c "st,stpmic1l" || ${HOSTTOOLS_DIR}/true) + if [ -f "${B}/${config}${soc_suffix}-${dt}/fdts/${dt}-bl2.dtb" ]; then + if [ $pcmi1_present -gt 0 ]; then + # st pmic1l is present, need to force to compilation with specific pcmi1l optionn + soc_extra_opt="${soc_extra_opt} STM32MP_STPMIC1L=1" + fi + fi + + # Init specific ddr settings + ddr_extra_opt="" + if [ "${TF_A_FWDDR}" = "1" ]; then + # Detect ddr type if it's present + if [ -f "${B}/${config}${soc_suffix}-${dt}/fdts/${dt}-bl2.dtb" ]; then + ddr_dtb_node=$(${STAGING_BINDIR_NATIVE}/fdtget -l ${B}/${config}${soc_suffix}-${dt}/fdts/${dt}-bl2.dtb /soc | grep ddr | head -n 1) + ddr_propertie=$(${STAGING_BINDIR_NATIVE}/fdtget ${B}/${config}${soc_suffix}-${dt}/fdts/${dt}-bl2.dtb /soc/${ddr_dtb_node} st,mem-name || echo "none") + ddr_target="" + # potentials value of ddr_propertie: + # DDR3 16bits + # DDR4 32bits + # DDR4 8Gbits + # LPDDR4 32bits + case ${ddr_propertie} in + DDR3*) + ddr_extra_opt=" STM32MP_DDR3_TYPE=1 " + ddr_target="ddr3" + ;; + DDR4*) + ddr_extra_opt=" STM32MP_DDR4_TYPE=1 " + ddr_target="ddr4" + ;; + LPDDR4*) + ddr_extra_opt=" STM32MP_LPDDR4_TYPE=1 " + ddr_target="lpddr4" + ;; + *) + bbfatal "Missing st,mem-name information for ${dt}" + ;; + esac + bbnote "${dt}: ${tf_a_make_target} -> ${ddr_extra_opt}" + # Copy TF-A ddr binary with explicit devicetree filename + if [ -s "${FWDDR_DIR}/${ddr_target}_pmu_train.bin" ]; then + install -m 644 "${FWDDR_DIR}/${ddr_target}_pmu_train.bin" "${B}/${config}${soc_suffix}-${dt}/${FWDDR_NAME}-${dt}-${config}.${FWDDR_SUFFIX}" + else + bbfatal "Missing ddr firmware file ${ddr_target}_pmu_train.bin for ${dt}" + fi + fi + fi + + encrypt_extra_opt="" + if [ "${ENCRYPT_ENABLE}" = "1" ]; then + encrypt_key="${ENCRYPT_FIP_KEY_PATH_LIST}" + if [ -n "${STM32MP_ENCRYPT_SOC_NAME}" ]; then + unset k + for soc in ${STM32MP_ENCRYPT_SOC_NAME}; do + k=$(expr $k + 1) + [ "$(echo ${dt} | grep -c ${soc})" -eq 1 ] && encrypt_key=$(echo ${ENCRYPT_FIP_KEY_PATH_LIST} | cut -d',' -f${k}) + done + fi + if [ "$(file "${encrypt_key}" | sed 's#.*: \(.*\)$#\1#')" = "ASCII text" ]; then + # The encryption key is already available in hexadecimal format, so just extract it from file + encrypt_key="$(cat ${encrypt_key})" + else + encrypt_key="$(hexdump -e '/1 "%02x"' ${encrypt_key})" + fi + encrypt_extra_opt="ENC_KEY=${encrypt_key}" + fi + + oe_runmake -C "${S}" BUILD_PLAT="${B}/${config}${soc_suffix}-${dt}" DTB_FILE_NAME="${dt}.dtb" ${extra_opt} ${soc_extra_opt} ${ddr_extra_opt} ${encrypt_extra_opt} ${tf_a_make_target} + if [ -f "${B}/${config}${soc_suffix}-${dt}/bl2.bin" ]; then + cp "${B}/${config}${soc_suffix}-${dt}/bl2.bin" "${B}/${config}${soc_suffix}-${dt}/${tfa_basename}-${dt}-${config}.bin" + fi + # Copy TF-A binary with explicit devicetree filename + if [ -f "${B}/${config}${soc_suffix}-${dt}/${tfa_basename}-${dt}.${TF_A_SUFFIX}" ]; then + cp "${B}/${config}${soc_suffix}-${dt}/${tfa_basename}-${dt}.${TF_A_SUFFIX}" "${B}/${config}${soc_suffix}-${dt}/${tfa_basename}-${dt}-${config}.${TF_A_SUFFIX}" + if [ "${TF_A_ENABLE_DEBUG_WRAPPER}" = "1" ]; then + stm32wrapper4dbg -s "${B}/${config}${soc_suffix}-${dt}/${tfa_basename}-${dt}.${TF_A_SUFFIX}" -d "${B}/${config}${soc_suffix}-${dt}/debug-${tfa_basename}-${dt}-${config}.${TF_A_SUFFIX}" + fi + + if [ "${SIGN_ENABLE}" = "1" ]; then + # Init sign key for signing tools + sign_key="${SIGN_KEY_PATH_LIST}" + if [ -n "${STM32MP_SOC_NAME}" ]; then + unset k + for soc in ${STM32MP_SOC_NAME}; do + k=$(expr $k + 1) + if [ "$(echo ${dt} | grep -c ${soc})" -eq 1 ] || [ "$(echo ${dt} | grep -c ${TF_A_SOC_MATCH})" -eq 1 ] ;then + sign_key=$(echo ${SIGN_KEY_PATH_LIST} | cut -d',' -f${k}) + fi + done + fi + # Init default encryption options for signing tool + tf_a_encrypt_opts="" + if [ "${ENCRYPT_ENABLE}" = "1" ]; then + # Init encrypt key for signing tools + encrypt_key="${ENCRYPT_FSBL_KEY_PATH_LIST}" + if [ -n "${STM32MP_ENCRYPT_SOC_NAME}" ]; then + unset k + for soc in ${STM32MP_ENCRYPT_SOC_NAME}; do + k=$(expr $k + 1) + [ "$(echo ${dt} | grep -c ${soc})" -eq 1 ] && encrypt_key=$(echo ${ENCRYPT_FSBL_KEY_PATH_LIST} | cut -d',' -f${k}) + done + fi + # Set encryption options for signing tools + tf_a_encrypt_opts="\ + --enc-key ${encrypt_key} \ + --enc-dc ${TF_A_ENCRYPT_DC} \ + --image-version ${TF_A_ENCRYPT_IMGVER} \ + " + fi + # Sign tf-a binary + bbnote "${SIGN_TOOL} \ + -bin "${B}/${config}${soc_suffix}-${dt}/${tfa_basename}-${dt}-${config}.${TF_A_SUFFIX}" \ + -o "${B}/${config}${soc_suffix}-${dt}/${tfa_basename}-${dt}-${config}${TF_A_ENCRYPT_SUFFIX}${TF_A_SIGN_SUFFIX}.${TF_A_SUFFIX}" \ + --password ${SIGN_KEY_PASS} \ + --public-key $(ls -1 $(dirname ${sign_key})/publicKey*.pem | tr '\n' '\t') \ + --private-key ${sign_key} \ + --type fsbl \ + --silent \ + ${SIGN_TOOL_EXTRA_soc} \ + ${tf_a_encrypt_opts} " + + ${SIGN_TOOL} \ + -bin "${B}/${config}${soc_suffix}-${dt}/${tfa_basename}-${dt}-${config}.${TF_A_SUFFIX}" \ + -o "${B}/${config}${soc_suffix}-${dt}/${tfa_basename}-${dt}-${config}${TF_A_ENCRYPT_SUFFIX}${TF_A_SIGN_SUFFIX}.${TF_A_SUFFIX}" \ + --password ${SIGN_KEY_PASS} \ + --public-key $(ls -1 $(dirname ${sign_key})/publicKey*.pem | tr '\n' '\t') \ + --private-key ${sign_key} \ + --type fsbl \ + --silent \ + ${SIGN_TOOL_EXTRA_soc} \ + ${tf_a_encrypt_opts} + if [ "${TF_A_ENABLE_DEBUG_WRAPPER}" = "1" ]; then + bbnote "${SIGN_TOOL} \ + -bin "${B}/${config}${soc_suffix}-${dt}/debug-${tfa_basename}-${dt}-${config}.${TF_A_SUFFIX}" \ + -o "${B}/${config}${soc_suffix}-${dt}/debug-${tfa_basename}-${dt}-${config}${TF_A_ENCRYPT_SUFFIX}${TF_A_SIGN_SUFFIX}.${TF_A_SUFFIX}" \ + --password ${SIGN_KEY_PASS} \ + --public-key $(ls -1 $(dirname ${sign_key})/publicKey*.pem | tr '\n' '\t') \ + --private-key "${sign_key}" \ + --type fsbl \ + --silent \ + ${SIGN_TOOL_EXTRA_soc} \ + ${tf_a_encrypt_opts}" + + ${SIGN_TOOL} \ + -bin "${B}/${config}${soc_suffix}-${dt}/debug-${tfa_basename}-${dt}-${config}.${TF_A_SUFFIX}" \ + -o "${B}/${config}${soc_suffix}-${dt}/debug-${tfa_basename}-${dt}-${config}${TF_A_ENCRYPT_SUFFIX}${TF_A_SIGN_SUFFIX}.${TF_A_SUFFIX}" \ + --password ${SIGN_KEY_PASS} \ + --public-key $(ls -1 $(dirname ${sign_key})/publicKey*.pem | tr '\n' '\t') \ + --private-key "${sign_key}" \ + --type fsbl \ + --silent \ + ${SIGN_TOOL_EXTRA_soc} \ + ${tf_a_encrypt_opts} + fi + fi + fi + done + done + + if [ "${TF_A_ENABLE_METADATA}" = "1" ]; then + rm -rf "${B}/${TF_A_METADATA_NAME}.${TF_A_METADATA_SUFFIX}" + ${TF_A_METADATA_TOOL} ${TF_A_METADATA_TOOL_ARGS} "${B}/${TF_A_METADATA_NAME}.${TF_A_METADATA_SUFFIX}" + fi +} + # This runs after 'tf_a_sysroot_populate()' which populates all # TF-A artifacts on the image deploy dir. # The purpose of this function is to create symlinks to the files needed From 5598c5973d430eb0fa64683a04238600ed47e614 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Thu, 13 Mar 2025 22:36:41 +0100 Subject: [PATCH 022/138] ccmp25-dvk: Force main DTB as the first item in the kernel device tree list for FIT images The kernel-fitimage class sets the default configuration using the first element from the kernel device tree list. This commit ensures that the main DTB is listed first to enforce the correct default configuration. https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- meta-digi-arm/conf/machine/ccmp25-dvk.conf | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/meta-digi-arm/conf/machine/ccmp25-dvk.conf b/meta-digi-arm/conf/machine/ccmp25-dvk.conf index e52935aed..be3b34b90 100644 --- a/meta-digi-arm/conf/machine/ccmp25-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp25-dvk.conf @@ -42,8 +42,10 @@ STM32MP_DT_FILES_EMMC = "ccmp25-dvk" STM32MP_DT_FILES_SDCARD = "${STM32MP_DT_FILES_EMMC}" STM32MP_DT_FILES_USB = "${STM32MP_DT_FILES_EMMC}" -# Extra DTB for board - need to specify it with .dtb ... +# Extra files for the board must be specified while ensuring the main DTB +# file for FIT image generation remains the first item. STM32MP_KERNEL_DEVICETREE:ccmp25-dvk += " \ + ccmp25-dvk.dtb \ ccmp25_bt.dtbo \ ccmp25_wifi.dtbo \ ccmp25-dvk_e55rb-i-mw346-c-mipi-dsi.dtbo \ @@ -56,7 +58,6 @@ STM32MP_KERNEL_DEVICETREE:ccmp25-dvk += " \ ccmp25-dvk_n25q256a-spi-nor-flash.dtbo \ ccmp25-dvk_nhd-3-5-640480ef-msxp-mipi-dsi.dtbo \ ccmp25-dvk_usb-3-0-typec.dtbo \ - ccmp25-dvk.dtb \ " # ========================================================================= From 18a376ff0fdad58518730e8ed9b7ba0016ad56f7 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Thu, 13 Mar 2025 22:54:16 +0100 Subject: [PATCH 023/138] ccmp2: add support to FIT image generation This commit introduces the necessary settings for the kernel-fitimage class to enable FIT image generation. https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- meta-digi-arm/conf/machine/include/ccmp2.inc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/meta-digi-arm/conf/machine/include/ccmp2.inc b/meta-digi-arm/conf/machine/include/ccmp2.inc index 70f4167f4..90b82496d 100644 --- a/meta-digi-arm/conf/machine/include/ccmp2.inc +++ b/meta-digi-arm/conf/machine/include/ccmp2.inc @@ -21,6 +21,12 @@ UBOOT_PREFIX = "fip" UBOOT_SUFFIX = "bin" # Set U-Boot FIP settings FIP_UBOOT_DTB = "u-boot" +# Include boot script into the FIT image +UBOOT_ENV = "boot" +# For FIT image usage +UBOOT_ENTRYPOINT = "0x88000000" +UBOOT_DTB_LOADADDRESS = "0x8a000000" +UBOOT_DTBO_LOADADDRESS = "0x8a000000" # ========================================================================= # trusted-firmware-a @@ -40,8 +46,8 @@ PREFERRED_PROVIDER_virtual/optee-os = "optee-os-stm32mp" # ========================================================================= # Kernel # ========================================================================= -KERNEL_IMAGETYPE = "${@bb.utils.contains('TRUSTFENCE_FIT_IMG', '1', 'fitImage', 'Image.gz', d)}" -KERNEL_CLASSES = "${@bb.utils.contains('TRUSTFENCE_FIT_IMG', '1', 'kernel-fitimage', 'kernel-uimage', d)}" +KERNEL_IMAGETYPE = "${@bb.utils.contains('TRUSTFENCE_SIGN_FIT_STM', '1', 'fitImage', 'Image.gz', d)}" +KERNEL_CLASSES = "kernel-fitimage" KERNEL_ALT_IMAGETYPE = "vmlinux" KERNEL_DEFCONFIG ?= "arch/arm64/configs/ccmp2_defconfig" From 9106a8c3825dfeb45c677394f50178dad189a9ec Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Thu, 13 Mar 2025 23:05:39 +0100 Subject: [PATCH 024/138] u-boot-dey: use common FIT signature fragment for stm platforms https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- .../u-boot/u-boot-dey/{ccmp1 => stm32mpcommon}/fit_signature.cfg | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/{ccmp1 => stm32mpcommon}/fit_signature.cfg (100%) diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/fit_signature.cfg b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/stm32mpcommon/fit_signature.cfg similarity index 100% rename from meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/fit_signature.cfg rename to meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/stm32mpcommon/fit_signature.cfg From f27c3364cbec02e4418e0dbc40475b9e0a5bf2b6 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Fri, 14 Mar 2025 10:33:09 +0100 Subject: [PATCH 025/138] meta-digi-arm: Add custom sign-stm32mp bbclass to fix build issue This commit imports the sign-stm32mp bbclass from the meta-st-stm32mp layer to allow customization. The main customization ensures that the search_path() function does not raise a build exception if the signing tool or keys are not present in the PATH before starting the build process. In our case, we do not need to manually install the tools or generate the keys beforehand, as this is automatically handled by Yocto in our DEY distribution. https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- .../classes/sign-stm32mp-digi.bbclass | 24 +++++++++++++++++++ meta-digi-arm/conf/layer.conf | 1 + .../fip-stm32mp/fip-stm32mp.bbappend | 3 +++ .../tf-a-stm32mp_2.10.bbappend | 3 +++ .../optee/optee-os-stm32mp_4.0.0.bbappend | 3 +++ 5 files changed, 34 insertions(+) create mode 100644 meta-digi-arm/classes/sign-stm32mp-digi.bbclass diff --git a/meta-digi-arm/classes/sign-stm32mp-digi.bbclass b/meta-digi-arm/classes/sign-stm32mp-digi.bbclass new file mode 100644 index 000000000..fbc94c4a8 --- /dev/null +++ b/meta-digi-arm/classes/sign-stm32mp-digi.bbclass @@ -0,0 +1,24 @@ +# Copyright (C) 2025, Digi International Inc. + +# Overwrite search_path() function in original 'sign-stm2mp.bbclass' +def search_path(file_search, d, err_not_found=False): + """ + Search for in BBPATH and return its absolute path. + If the file is not found: + - Returns the original file string if err_not_found is False. + - Otherwise, it triggers a fatal error. + """ + search_path = d.getVar("BBPATH").split(":") + for p in search_path: + file_path = os.path.join(p, file_search) + if os.path.isfile(file_path): + return file_path + + # If file is not found + bbpaths = d.getVar('BBPATH').replace(':','\n\t') + bb.debug(1, '\n[sign-stm32mp-digi] Not able to find "%s" path from current BBPATH' % (file_search)) + + if not err_not_found: + return file_search # Return original file string instead of failing + + bb.fatal('\n[sign-stm32mp-digi] Not able to find "%s" path from current BBPATH var:\n\t%s.' % (file_search, bbpaths)) diff --git a/meta-digi-arm/conf/layer.conf b/meta-digi-arm/conf/layer.conf index 49a615cc7..0ba8036d9 100644 --- a/meta-digi-arm/conf/layer.conf +++ b/meta-digi-arm/conf/layer.conf @@ -48,6 +48,7 @@ BBMASK += " \ meta-st-stm32mp/recipes-core/busybox/busybox_%.bbappend \ meta-st-stm32mp/recipes-core/systemd/systemd-conf_%.bbappend \ meta-st-stm32mp/recipes-kernel/linux/linux-stm32mp_5.15.bb \ + meta-st-stm32mp/recipes-bsp/u-boot/u-boot-stm32mp_2023.10.bb \ " # Tweak x-linux-ai dependencies to use ST's machine learning packages diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend index a183eee65..1ebaaca2f 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend @@ -2,6 +2,9 @@ # Copyright (C) 2024,2025, Digi International Inc. # +# Inherit custom DIGI sign class to skip signing tool and key parsing restrictions +inherit sign-stm32mp-digi + # Add optee-usb FIP configuration STM32MP_DEVICETREE_USB = " ${@' '.join('%s' % dt_file for dt_file in list(dict.fromkeys((d.getVar('STM32MP_DT_FILES_USB') or '').split())))} " FIP_CONFIG[optee-usb] ?= "optee,${STM32MP_DEVICETREE_USB},default:optee,usb" diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend index d84e689f6..da16a3e3a 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend @@ -2,6 +2,9 @@ # Copyright (C) 2022-2025, Digi International Inc. # +# Inherit custom DIGI sign class to skip signing tool and key parsing restrictions +inherit sign-stm32mp-digi + # Select internal or Github TF-A repo TFA_URI_STASH = "${DIGI_MTK_GIT}/emp/arm-trusted-firmware.git;protocol=ssh" TFA_URI_GITHUB = "${DIGI_GITHUB_GIT}/arm-trusted-firmware.git;protocol=https" diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-os-stm32mp_4.0.0.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-os-stm32mp_4.0.0.bbappend index 091854899..c57ce1a4a 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-os-stm32mp_4.0.0.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-os-stm32mp_4.0.0.bbappend @@ -2,6 +2,9 @@ # Copyright (C) 2022-2025, Digi International Inc. # +# Inherit custom DIGI sign class to skip signing tool and key parsing restrictions +inherit sign-stm32mp-digi + # Select internal or Github OPTEE repo OPTEE_URI_STASH = "${DIGI_MTK_GIT}/emp/optee_os.git;protocol=ssh" OPTEE_URI_GITHUB = "${DIGI_GITHUB_GIT}/optee_os.git;protocol=https" From a472b4f2f06416361dcc7e6d4c160e19bd14799d Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Mon, 17 Mar 2025 21:07:01 +0100 Subject: [PATCH 026/138] linux-dey-6.6: fix FIT image generation Assembling a FIT image requires that all artifacts are ready before calling assemble_fitimage(), which occurs before do_install() and after do_compile(). To ensure the Linux kernel recipe provides the final artifacts in the correct location, this commit replaces the additional do_install() step with an additional do_compile() step, ensuring that all necessary artifacts are available before assemble_fitimage(). https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- meta-digi-arm/recipes-kernel/linux/linux-dey_6.6.bb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 fd9255511..36cc85a0c 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 @@ -29,7 +29,7 @@ module_conf_btnxpuart = "blacklist btnxpuart" # --------------------------------------------------------------------- # stub for devicetree which are located on digi directory -do_install:prepend:ccmp2() { +do_compile:append:ccmp2() { if [ -d "${B}/arch/${ARCH}/boot/dts/digi" ]; then for dtbf in ${KERNEL_DEVICETREE}; do install -m 0644 "${B}/arch/${ARCH}/boot/dts/digi/${dtbf}" "${B}/arch/${ARCH}/boot/dts/" From f8c0274076c41da667a56c0b47fac1a7e2a2d19b Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Mon, 17 Mar 2025 21:55:56 +0100 Subject: [PATCH 027/138] stm-st-stm32mp: fip/tf-a: fix symlink generation This commit generalizes the symlink generation to allow creating a final symlink in the deploy directory, supporting different artifact flavors: regular, signed or encrypted. https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- .../recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend | 8 +++++--- .../trusted-firmware-a/tf-a-stm32mp_2.10.bbappend | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend index 1ebaaca2f..84677e211 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend @@ -174,16 +174,18 @@ deploy_symlinks_fip() { i="$(expr ${i} + 1)" dt_config=$(echo ${FIP_DEVICETREE} | cut -d',' -f${i}) for dt in ${dt_config}; do - FIP_FILENAME="${FIP_BASENAME}-${dt}-${config}${FIP_SIGN_SUFFIX}.${FIP_SUFFIX}" + FIP_FILENAME="${FIP_BASENAME}-${dt}-${config}${FIP_ENCRYPT_SUFFIX}${FIP_SIGN_SUFFIX}.${FIP_SUFFIX}" if [ -f "${DEPLOY_DIR_IMAGE}/${FIP_BASEDIR}/${FIP_FILENAME}" ]; then cd "${DEPLOY_DIR_IMAGE}" # symlink FIP ln -sf "${FIP_BASEDIR}/${FIP_FILENAME}" "${DEPLOY_DIR_IMAGE}/" fi - if [ -f "${DEPLOY_DIR_IMAGE}/${FIP_BASEDIR}/${FIP_BASENAME}-${dt}-ddr-${config}.${FWDDR_SUFFIX}" ]; then + + FIP_DDR_FILENAME="${FIP_BASENAME}-${dt}-ddr-${config}${FIP_ENCRYPT_SUFFIX}${FIP_SIGN_SUFFIX}.${FWDDR_SUFFIX}" + if [ -f "${DEPLOY_DIR_IMAGE}/${FIP_BASEDIR}/${FIP_DDR_FILENAME}" ]; then cd "${DEPLOY_DIR_IMAGE}" # symlink DDR firmware (needed for USB recovery) - ln -sf "${FIP_BASEDIR}/${FIP_BASENAME}-${dt}-ddr-${config}.${FWDDR_SUFFIX}" "${DEPLOY_DIR_IMAGE}/" + ln -sf "${FIP_BASEDIR}/${FIP_DDR_FILENAME}" "${DEPLOY_DIR_IMAGE}/" fi done done diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend index da16a3e3a..48ddd4170 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend @@ -313,11 +313,11 @@ deploy_symlinks_atf() { dt_config=$(echo ${TF_A_DEVICETREE} | cut -d',' -f${i}) tfa_basename=$(echo ${TF_A_BINARIES} | cut -d',' -f${i}) for dt in ${dt_config}; do - TF_A_FILENAME="${tfa_basename}-${dt}-${config}.${TF_A_SUFFIX}" + TF_A_FILENAME="${tfa_basename}-${dt}-${config}${TF_A_ENCRYPT_SUFFIX}${TF_A_SIGN_SUFFIX}.${TF_A_SUFFIX}" if [ -f "${DEPLOY_DIR_IMAGE}/${TF_A_BASEDIR}/${TF_A_FILENAME}" ]; then cd "${DEPLOY_DIR_IMAGE}" # symlink TF-A - ln -sf "${TF_A_BASEDIR}/${TF_A_FILENAME}" "${DEPLOY_DIR_IMAGE}/" + ln -sf "${TF_A_BASEDIR}/${TF_A_FILENAME}" "${DEPLOY_DIR_IMAGE}" fi done done From f0c4cdc5bbf4fe0a7780c6c9461c59d717ac979e Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Fri, 21 Mar 2025 13:32:24 +0100 Subject: [PATCH 028/138] stm-st-stm32mp: fip-stm32mp: fix symlinks in DEPLOYDIR This commit addresses two issues in the symlink deployment function: - do_deploy() should not write directly to DEPLOY_DIR_IMAGE. Instead, it now uses DEPLOYDIR. - Expands do_deploy() instead of using SYSROOT_PREPROCESS_FUNCS to ensure that the original FIP artifacts are created and properly regenerated on each build. https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- .../fip-stm32mp/fip-stm32mp.bbappend | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend index 84677e211..d960bfbe0 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend @@ -160,12 +160,12 @@ addtask deploy before do_build after do_compile FWDDR_SUFFIX ?= "bin" -# This runs after 'sysroot_populate()' which populates all +# This runs after 'do_compile()' which populates all # FIP artifacts on the image deploy dir. # The purpose of this function is to create symlinks to the files needed # by the uuu installer that are located in subdirectories. -deploy_symlinks_fip() { - # Create symlinks in DEPLOY_DIR_IMAGE +do_deploy:append() { + # Create symlinks in DEPLOYDIR # Remove trailing slash (/) from ST variables FIP_BASEDIR="$(echo ${FIP_DIR_FIP} | cut -c2-)" @@ -175,19 +175,18 @@ deploy_symlinks_fip() { dt_config=$(echo ${FIP_DEVICETREE} | cut -d',' -f${i}) for dt in ${dt_config}; do FIP_FILENAME="${FIP_BASENAME}-${dt}-${config}${FIP_ENCRYPT_SUFFIX}${FIP_SIGN_SUFFIX}.${FIP_SUFFIX}" - if [ -f "${DEPLOY_DIR_IMAGE}/${FIP_BASEDIR}/${FIP_FILENAME}" ]; then - cd "${DEPLOY_DIR_IMAGE}" + if [ -f "${DEPLOYDIR}/${FIP_BASEDIR}/${FIP_FILENAME}" ]; then + cd "${DEPLOYDIR}" # symlink FIP - ln -sf "${FIP_BASEDIR}/${FIP_FILENAME}" "${DEPLOY_DIR_IMAGE}/" + ln -sf "${FIP_BASEDIR}/${FIP_FILENAME}" "${DEPLOYDIR}/" fi FIP_DDR_FILENAME="${FIP_BASENAME}-${dt}-ddr-${config}${FIP_ENCRYPT_SUFFIX}${FIP_SIGN_SUFFIX}.${FWDDR_SUFFIX}" - if [ -f "${DEPLOY_DIR_IMAGE}/${FIP_BASEDIR}/${FIP_DDR_FILENAME}" ]; then - cd "${DEPLOY_DIR_IMAGE}" + if [ -f "${DEPLOYDIR}/${FIP_BASEDIR}/${FIP_DDR_FILENAME}" ]; then + cd "${DEPLOYDIR}" # symlink DDR firmware (needed for USB recovery) - ln -sf "${FIP_BASEDIR}/${FIP_DDR_FILENAME}" "${DEPLOY_DIR_IMAGE}/" + ln -sf "${FIP_BASEDIR}/${FIP_DDR_FILENAME}" "${DEPLOYDIR}/" fi done done } -SYSROOT_PREPROCESS_FUNCS += "deploy_symlinks_fip" From 250acb904163d1ab088e0d41887c2cb19be49f19 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Mon, 24 Mar 2025 09:54:10 +0100 Subject: [PATCH 029/138] stm-st-stm32mp: fip-stm32mp: Integrate do_deploy:append into main do_deploy() function This commit simplifies the recipe by integrating the symlink generation directly into the main do_deploy() function, removing the need to maintain both a do_deploy() and a do_deploy:append(). https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- .../fip-stm32mp/fip-stm32mp.bbappend | 61 +++++++++---------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend index d960bfbe0..a81d717a9 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/fip-stm32mp/fip-stm32mp.bbappend @@ -21,10 +21,13 @@ python set_fip_sign_key() { if (p): d.setVar('SIGN_KEY_PASS', p) } + # Addons parameters for FIP_WRAPPER FIP_SOC_SEARCH ?= " ${STM32MP_SOC_NAME} " FIP_SOC_MATCH ?= " ${DIGI_SOM} " +FWDDR_SUFFIX ?= "bin" + # Deploy the fip binary for current target do_deploy() { install -d ${DEPLOYDIR}/${FIP_DIR_FIP} @@ -155,38 +158,30 @@ do_deploy() { --output ${DEPLOYDIR}/${FIP_DIR_FIP} done done + + # Create symlinks in DEPLOYDIR + + # Remove trailing slash (/) from ST variables + FIP_BASEDIR="$(echo ${FIP_DIR_FIP} | cut -c2-)" + unset i + for config in ${FIP_CONFIG}; do + i="$(expr ${i} + 1)" + dt_config=$(echo ${FIP_DEVICETREE} | cut -d',' -f${i}) + for dt in ${dt_config}; do + FIP_FILENAME="${FIP_BASENAME}-${dt}-${config}${FIP_ENCRYPT_SUFFIX}${FIP_SIGN_SUFFIX}.${FIP_SUFFIX}" + if [ -f "${DEPLOYDIR}/${FIP_BASEDIR}/${FIP_FILENAME}" ]; then + cd "${DEPLOYDIR}" + # symlink FIP + ln -sf "${FIP_BASEDIR}/${FIP_FILENAME}" "${DEPLOYDIR}/" + fi + + FIP_DDR_FILENAME="${FIP_BASENAME}-${dt}-ddr-${config}${FIP_ENCRYPT_SUFFIX}${FIP_SIGN_SUFFIX}.${FWDDR_SUFFIX}" + if [ -f "${DEPLOYDIR}/${FIP_BASEDIR}/${FIP_DDR_FILENAME}" ]; then + cd "${DEPLOYDIR}" + # symlink DDR firmware (needed for USB recovery) + ln -sf "${FIP_BASEDIR}/${FIP_DDR_FILENAME}" "${DEPLOYDIR}/" + fi + done + done } addtask deploy before do_build after do_compile - -FWDDR_SUFFIX ?= "bin" - -# This runs after 'do_compile()' which populates all -# FIP artifacts on the image deploy dir. -# The purpose of this function is to create symlinks to the files needed -# by the uuu installer that are located in subdirectories. -do_deploy:append() { - # Create symlinks in DEPLOYDIR - - # Remove trailing slash (/) from ST variables - FIP_BASEDIR="$(echo ${FIP_DIR_FIP} | cut -c2-)" - unset i - for config in ${FIP_CONFIG}; do - i="$(expr ${i} + 1)" - dt_config=$(echo ${FIP_DEVICETREE} | cut -d',' -f${i}) - for dt in ${dt_config}; do - FIP_FILENAME="${FIP_BASENAME}-${dt}-${config}${FIP_ENCRYPT_SUFFIX}${FIP_SIGN_SUFFIX}.${FIP_SUFFIX}" - if [ -f "${DEPLOYDIR}/${FIP_BASEDIR}/${FIP_FILENAME}" ]; then - cd "${DEPLOYDIR}" - # symlink FIP - ln -sf "${FIP_BASEDIR}/${FIP_FILENAME}" "${DEPLOYDIR}/" - fi - - FIP_DDR_FILENAME="${FIP_BASENAME}-${dt}-ddr-${config}${FIP_ENCRYPT_SUFFIX}${FIP_SIGN_SUFFIX}.${FWDDR_SUFFIX}" - if [ -f "${DEPLOYDIR}/${FIP_BASEDIR}/${FIP_DDR_FILENAME}" ]; then - cd "${DEPLOYDIR}" - # symlink DDR firmware (needed for USB recovery) - ln -sf "${FIP_BASEDIR}/${FIP_DDR_FILENAME}" "${DEPLOYDIR}/" - fi - done - done -} From 30751b0eef4166ad6b80a5b935336418ee493d3b Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Tue, 18 Mar 2025 17:52:48 +0100 Subject: [PATCH 030/138] stm-st-stm32mp: add optee-client recipe based on v4.0.0 This commit integrates optee-client support from the meta-st-openstlinux layer, based on the openstlinux-6.6-yocto-scarthgap-mpu-v25.03.19 tag. https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- meta-digi-arm/conf/machine/include/ccmp2.inc | 2 + .../optee-client/create-tee-supplicant-env | 21 +++++++ .../optee/optee-client/optee-udev.rules | 2 + .../optee/optee-client/tee-supplicant.service | 16 +++++ .../optee/optee-client_4.0.bb | 58 +++++++++++++++++++ 5 files changed, 99 insertions(+) create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client/create-tee-supplicant-env create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client/optee-udev.rules create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client/tee-supplicant.service create mode 100755 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client_4.0.bb diff --git a/meta-digi-arm/conf/machine/include/ccmp2.inc b/meta-digi-arm/conf/machine/include/ccmp2.inc index 90b82496d..a5020a602 100644 --- a/meta-digi-arm/conf/machine/include/ccmp2.inc +++ b/meta-digi-arm/conf/machine/include/ccmp2.inc @@ -42,6 +42,8 @@ TF_A_METADATA_TOOL_ARGS ?= "-g -i 1 -b 2 -v ${TF_A_METADATA_TOOL_VERSION} ${DEVI # optee-os # ========================================================================= PREFERRED_PROVIDER_virtual/optee-os = "optee-os-stm32mp" +# OPTEE runtime packages to install +OPTEE_PKGS ??= "optee-client" # ========================================================================= # Kernel diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client/create-tee-supplicant-env b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client/create-tee-supplicant-env new file mode 100644 index 000000000..c9db5584e --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client/create-tee-supplicant-env @@ -0,0 +1,21 @@ +#!/bin/sh +# +# Create a systemd environment file for tee-supplicant +# $1 is the path to the file to be generated. +# At the moment this figures out the --rpmb-cid parameter to be given to +# tee-supplicant, indicating which eMMC device OP-TEE should use for RPMB +# storage. +# No file is generated if no device is found (not an error) or if multiple +# eMMCs are found (which is an error). + +[ "$1" ] || { echo Usage: $0 FILE >&2; exit 1; } + +touch $1 + +for f in /sys/class/mmc_host/mmc*/mmc*\:*/mmcblk?rmpb; do + [ "$CID" ] && { echo $0: Multiple eMMC devices found, not chosing one automatically >&2; exit 2; } + # POSIX shells don't expand globbing patterns that match no file + [ -e $f ] || exit 0 + CID=$(cat $(dirname $f)/cid) +done +[ "$CID" ] && echo RPMB_CID="--rpmb-cid $CID" >$1 diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client/optee-udev.rules b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client/optee-udev.rules new file mode 100644 index 000000000..63101d993 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client/optee-udev.rules @@ -0,0 +1,2 @@ +KERNEL=="tee[0-9]*", MODE="0660", OWNER="root", GROUP="teeclnt", TAG+="systemd", ENV{SYSTEMD_WANTS}+="tee.device" +KERNEL=="teepriv[0-9]*", MODE="0660", OWNER="root", GROUP="tee", TAG+="systemd", ENV{SYSTEMD_WANTS}+="teepriv.device" diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client/tee-supplicant.service b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client/tee-supplicant.service new file mode 100644 index 000000000..bfcc56b6e --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client/tee-supplicant.service @@ -0,0 +1,16 @@ +[Unit] +Description=TEE Supplicant +Wants=dev-tee0.device +After=dev-tee0.device +After=systemd-udevd.service + +[Service] +User=tee +Group=tee +EnvironmentFile=-/etc/default/tee-supplicant +ExecStartPre=/usr/sbin/create-tee-supplicant-env /tmp/tee-supplicant.env +EnvironmentFile=-/tmp/tee-supplicant.env +ExecStart=/usr/sbin/tee-supplicant $RPMB_CID $OPTARGS + +[Install] +WantedBy=basic.target diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client_4.0.bb b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client_4.0.bb new file mode 100755 index 000000000..1fde6e3ff --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client_4.0.bb @@ -0,0 +1,58 @@ +SUMMARY = "OPTEE Client" +HOMEPAGE = "https://github.com/OP-TEE/optee_client" + +LICENSE = "BSD-2-Clause" +LIC_FILES_CHKSUM = "file://${S}/LICENSE;md5=69663ab153298557a59c67a60a743e5b" + +inherit python3native systemd cmake pkgconfig + +SRC_URI = "git://github.com/OP-TEE/optee_client.git;protocol=https;branch=master \ + file://tee-supplicant.service \ + file://create-tee-supplicant-env \ + file://optee-udev.rules \ + " + +SRCREV = "acb0885c117e73cb6c5c9b1dd9054cb3f93507ee" + +PV = "4.0.0+git${SRCPV}" + +S = "${WORKDIR}/git" + +DEPENDS += "util-linux-libuuid" + +SYSTEMD_SERVICE:${PN} = "tee-supplicant.service" + +EXTRA_OECMAKE = " \ + -DCFG_TEE_FS_PARENT_PATH='${localstatedir}/lib/tee' \ + -DCFG_WERROR=OFF \ + -DCFG_TEE_CLIENT_LOG_LEVEL=2 \ + -DCFG_TEE_CLIENT_LOG_FILE='/data/tee/teec.log' \ + -DBUILD_SHARED_LIBS=ON \ + -DRPMB_EMU=0 \ + " + +do_install:append() { + if ${@bb.utils.contains('DISTRO_FEATURES','systemd','true','false',d)}; then + sed -i -e s:/etc:${sysconfdir}:g \ + -e s:/usr/bin:${bindir}:g \ + ${WORKDIR}/tee-supplicant.service + + install -D -p -m0644 ${WORKDIR}/tee-supplicant.service ${D}${systemd_system_unitdir}/tee-supplicant.service + install -D -p -m0755 ${WORKDIR}/create-tee-supplicant-env ${D}${sbindir}/ + fi + install -d ${D}${sysconfdir}/udev/rules.d + install -m 0644 ${WORKDIR}/optee-udev.rules ${D}${sysconfdir}/udev/rules.d/optee.rules + install -d -m770 -o root -g tee ${D}${localstatedir}/lib/tee +} +FILES:${PN} += "${sysconfdir} ${localstatedir}" + +inherit useradd + +USERADD_PACKAGES = "${PN}" +# Create groups 'tee' and 'teeclnt'. Permissions are set elsewhere on +# /dev/teepriv0 and /dev/tee0 so that tee-supplicant should run as a user that +# is a member of the 'tee' group, and TEE client applications should runs as a +# user that is a member of the 'teeclnt' group. +GROUPADD_PARAM:${PN} = "--system tee; --system teeclnt" +# Create user 'tee' member of group 'tee' to run tee-supplicant +USERADD_PARAM:${PN} = "--system -d / -M -s /bin/nologin -c 'User for tee-supplicant' -g tee tee" From 4be874e30f7f634b78ccf085db95d0a4e78cd62d Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Tue, 18 Mar 2025 17:58:53 +0100 Subject: [PATCH 031/138] trustfence-fscrypt: add support to STM32MP platforms based on libteec v1.0.0 This commit includes a new tarball based on trustfence-fscrypt v0.2, cross-compiled against libteec v1.0.0, to enable support for STM32MP platforms. https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- .../recipes-core/trustfence/trustfence-fscrypt_0.2.bb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/meta-digi-dey/recipes-core/trustfence/trustfence-fscrypt_0.2.bb b/meta-digi-dey/recipes-core/trustfence/trustfence-fscrypt_0.2.bb index 80619d560..e7eebf779 100644 --- a/meta-digi-dey/recipes-core/trustfence/trustfence-fscrypt_0.2.bb +++ b/meta-digi-dey/recipes-core/trustfence/trustfence-fscrypt_0.2.bb @@ -1,4 +1,4 @@ -# Copyright (C) 2024 Digi International. +# Copyright (C) 2024,2025 Digi International Inc. SUMMARY = "Trustfence fscrypt command line tool" SECTION = "console/tools" @@ -13,6 +13,11 @@ SRC_URI[aarch64.sha256sum] = "6c7846389f1b30d12b9a8f08bf09db3ed44532c61625d67d1b SRC_URI[arm.md5sum] = "0831130450d6f0beeebbb68af9b6af29" SRC_URI[arm.sha256sum] = "7dee4bbcff21d817bbbc152e904e8091362378446b08ad2d485f373b0da8b83b" +# Trustfence fscrypt based on libteec v1.0.0 +SRC_URI:stm32mpcommon = "${DIGI_PKG_SRC}/${BP}-${TF_FSCRYPT_ARCH}-libteec-v1.0.0.tar.gz;name=${TARGET_ARCH}-libteecv1" +SRC_URI[aarch64-libteecv1.md5sum] = "538f3bff6c9cd6110b7207581e4300d3" +SRC_URI[aarch64-libteecv1.sha256sum] = "a15a657f7350f9f5c9d39eb0f5d634174c1bc22d7e50333cc01de295e95c0fa4" + # Needed to resolve dependencies to libteec RDEPENDS:${PN} += "optee-client" From c59a5fdfe9d8766d486b7f60fa81e85699d60dd5 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Mon, 24 Mar 2025 13:09:59 +0100 Subject: [PATCH 032/138] u-boot-dey: ccmp2: fix support for flashing Trustfence artifacts Both boot artifacts now use the same signed label, so this commit removes unnecessary logic and updates the UUU install script to support the new boot artifact naming. It also fixes the detection of signed artifacts by checking for the presence of the FIP artifact, instead of relying on a U-BOOT artifact that does not exist on this platform. https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc | 4 ---- .../u-boot/u-boot-dey/ccmp2/install_linux_fw_uuu.sh | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc index 58f4abc40..8bcb66272 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc @@ -95,13 +95,9 @@ build_uboot_scripts() { fi else sed -i -e 's,##SIGNED##,_Signed,g' ${TMP_INSTALL_SCR} - sed -i -e 's,##SIGNED_TFA##,_signed,g' ${TMP_INSTALL_SCR} fi else sed -i -e 's,-##SIGNED##,,g' -e 's,##SIGNED##,,g' ${TMP_INSTALL_SCR} - if [ "${DEY_SOC_VENDOR}" = "STM" ]; then - sed -i -e 's,##SIGNED_TFA##,,g' ${TMP_INSTALL_SCR} - fi fi if [ "${f_ext}" = "txt" ]; then mkimage -T script -n "DEY firmware install script" -C none -d ${TMP_INSTALL_SCR} ${DEPLOYDIR}/${f%.*}.scr diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_uuu.sh b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_uuu.sh index a4bb3eeda..dc2c4b0f0 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_uuu.sh +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_uuu.sh @@ -108,7 +108,7 @@ echo "Determining image files to use..." # Determine ATF file to program if [ -z "${INSTALL_ATF_FILENAME}" ]; then - INSTALL_ATF_FILENAME="tf-a-##MACHINE##-optee-emmc.stm32##SIGNED_TFA##" + INSTALL_ATF_FILENAME="tf-a-##MACHINE##-optee-emmc##SIGNED##.stm32" fi INSTALL_METADATA_FILENAME="metadata-##MACHINE##.bin" @@ -306,7 +306,7 @@ else fi # Set the dboot_kernel_var to fitimage if Trustfence is enabled -if [ "${TRUSTFENCE}" = "true" ] || echo "${INSTALL_UBOOT_FILENAME}" | grep -q -e "signed"; then +if [ "${TRUSTFENCE}" = "true" ] || echo "${INSTALL_FIP_FILENAME}" | grep -q -e "Signed"; then uuu fb: ucmd setenv dboot_kernel_var fitimage uuu fb: ucmd saveenv fi From ff97a34b64c532db8525258b5dd3b3deda0faec0 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Mon, 24 Mar 2025 13:28:02 +0100 Subject: [PATCH 033/138] ccmp25-dvk: add support to include Trustfence artifacts in ZIP installer This commit conditionally selects the appropriate boot artifacts to include in the ZIP installer, depending on whether Trustfence is enabled or not. https://onedigi.atlassian.net/browse/DEL-9442 Signed-off-by: Arturo Buzarra --- meta-digi-arm/conf/machine/ccmp25-dvk.conf | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/meta-digi-arm/conf/machine/ccmp25-dvk.conf b/meta-digi-arm/conf/machine/ccmp25-dvk.conf index be3b34b90..728e5f9e8 100644 --- a/meta-digi-arm/conf/machine/ccmp25-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp25-dvk.conf @@ -144,9 +144,11 @@ ST_USERFS = "0" # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = " \ - tf-a-ccmp25-dvk-optee-emmc.stm32 \ + ${@oe.utils.ifelse(d.getVar('TRUSTFENCE_SIGN') == '1', 'tf-a-ccmp25-dvk-optee-emmc${SIGN_SUFFIX}.stm32', \ + 'tf-a-ccmp25-dvk-optee-emmc.stm32')} \ metadata-ccmp25-dvk.bin \ - fip-ccmp25-dvk-optee-emmc.bin \ + ${@oe.utils.ifelse(d.getVar('TRUSTFENCE_SIGN') == '1', 'fip-ccmp25-dvk-optee-emmc${SIGN_SUFFIX}.bin', \ + 'fip-ccmp25-dvk-optee-emmc.bin')} \ " # Per-machine DISTRO_FEATURES customization From 6d01ab78f9a2292b08dcb36ffed2462ea7208198 Mon Sep 17 00:00:00 2001 From: Christophe Priouzeau Date: Wed, 12 Feb 2025 13:00:55 +0100 Subject: [PATCH 034/138] stm-st-stm32mp: gstreamer-plugins-bad: v4l2codecs: add support of encoding from RGBA Add also BGRA, ARGB, ABGR formats in order to be aligned with stateful v4l2 codecs such as v4l2jpegenc. https://onedigi.atlassian.net/browse/DEL-9546 Change-Id: Ia51f7a93b0a6fbccb3222230e9f0936d2293cbbb Reviewed-on: https://gerrit.st.com/c/oeivi/oe/st/meta-opensdk/+/436379 Reviewed-by: Christophe PRIOUZEAU ACI: CITOOLS Tested-by: Christophe PRIOUZEAU Signed-off-by: Arturo Buzarra --- ...cs-add-support-of-encoding-from-RGBA.patch | 48 +++++++++++++++++++ .../gstreamer1.0-plugins-bad_1.22.12.bbappend | 1 + 2 files changed, 49 insertions(+) create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0081-v4l2codecs-add-support-of-encoding-from-RGBA.patch diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0081-v4l2codecs-add-support-of-encoding-from-RGBA.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0081-v4l2codecs-add-support-of-encoding-from-RGBA.patch new file mode 100644 index 000000000..cb819dd5c --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0081-v4l2codecs-add-support-of-encoding-from-RGBA.patch @@ -0,0 +1,48 @@ +From b8fd1214cd9e9651ea076fdeabb98dcee5b3354f Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Wed, 30 Oct 2024 16:22:00 +0100 +Subject: [PATCH] v4l2codecs: add support of encoding from RGBA + +Add support of encoding from RGBA, BGRA, ARGB, ABGR + +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2format.c | 4 ++++ + sys/v4l2codecs/gstv4l2format.h | 2 +- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/sys/v4l2codecs/gstv4l2format.c b/sys/v4l2codecs/gstv4l2format.c +index 33cef56..2d930d1 100644 +--- a/sys/v4l2codecs/gstv4l2format.c ++++ b/sys/v4l2codecs/gstv4l2format.c +@@ -42,10 +42,14 @@ static struct FormatEntry format_map[] = { + {V4L2_PIX_FMT_P010, 1, GST_VIDEO_FORMAT_P010_10LE, 16, 420}, + {V4L2_PIX_FMT_RGB565, 1, GST_VIDEO_FORMAT_RGB16, 16, 0}, + {V4L2_PIX_FMT_XBGR32, 1, GST_VIDEO_FORMAT_BGRx, 32, 0}, ++ {V4L2_PIX_FMT_ABGR32, 1, GST_VIDEO_FORMAT_BGRA, 32, 0}, + {V4L2_PIX_FMT_BGR32, 1, GST_VIDEO_FORMAT_BGRx, 32, 0}, + {V4L2_PIX_FMT_RGBX32, 1, GST_VIDEO_FORMAT_RGBx, 32, 0}, ++ {V4L2_PIX_FMT_RGBA32, 1, GST_VIDEO_FORMAT_RGBA, 32, 0}, + {V4L2_PIX_FMT_BGRX32, 1, GST_VIDEO_FORMAT_xBGR, 32, 0}, ++ {V4L2_PIX_FMT_BGRA32, 1, GST_VIDEO_FORMAT_ABGR, 32, 0}, + {V4L2_PIX_FMT_XRGB32, 1, GST_VIDEO_FORMAT_xRGB, 32, 0}, ++ {V4L2_PIX_FMT_ARGB32, 1, GST_VIDEO_FORMAT_ARGB, 32, 0}, + {V4L2_PIX_FMT_RGB32, 1, GST_VIDEO_FORMAT_xRGB, 32, 0}, + {0,} + }; +diff --git a/sys/v4l2codecs/gstv4l2format.h b/sys/v4l2codecs/gstv4l2format.h +index 1f634ee..2bf675f 100644 +--- a/sys/v4l2codecs/gstv4l2format.h ++++ b/sys/v4l2codecs/gstv4l2format.h +@@ -24,7 +24,7 @@ + #include + #include "linux/videodev2.h" + +-#define GST_V4L2_DEFAULT_VIDEO_FORMATS "{ NV12, YUY2, NV12_4L4, NV12_32L32, NV12_16L32S, I420, P010_10LE, RGB16, BGRx, RGBx, xBGR, xRGB}" ++#define GST_V4L2_DEFAULT_VIDEO_FORMATS "{ NV12, YUY2, NV12_4L4, NV12_32L32, NV12_16L32S, I420, P010_10LE, RGB16, BGRx, BGRA, RGBx, RGBA, xBGR, ABGR, xRGB, ARGB}" + + gboolean gst_v4l2_format_to_video_info (struct v4l2_format * fmt, + GstVideoInfo * out_info); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend index fb0a82ad1..b160980e7 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend @@ -75,6 +75,7 @@ SRC_URI:append = " \ file://0078-gtkwaylandsink-Add-gst_buffer_pool_config_set_params.patch \ file://0079-waylandsink-config-buffer-pool-with-query-size-when-.patch \ file://0080-gtkwaylandsink-config-buffer-pool-with-query-size-wh.patch \ + file://0081-v4l2codecs-add-support-of-encoding-from-RGBA.patch \ " PACKAGECONFIG_GL ?= "${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'gles2 egl', '', d)}" From 1d58c7138e0ef81fd2333e82242c5ad722d3063f Mon Sep 17 00:00:00 2001 From: Christophe Priouzeau Date: Wed, 12 Feb 2025 13:01:41 +0100 Subject: [PATCH 035/138] stm-st-stm32mp: gstreamer-plugins-bad: revert part of (gtk)waylandsink: fix QVGA YUV420 planar This cause issue with gst-launch-1.0 libcamerasrc ! video/x-raw, format=RGB16, width=800, height=600 ! queue ! waylandsink zwp_linux_buffer_params_v1@14: error 6: invalid buffer stride or height for plane 0 ERROR: from element /GstPipeline:pipeline0/GstWaylandSink:waylandsink0: Video memory can not be read from userspace. https://onedigi.atlassian.net/browse/DEL-9546 Change-Id: Ic82e3787dd74642426baea3d35c2d778f4b1da14 Signed-off-by: Hugues Fruchet Reviewed-on: https://gerrit.st.com/c/oeivi/oe/st/meta-opensdk/+/435172 ACI: CITOOLS Tested-by: Christophe PRIOUZEAU Reviewed-by: Christophe PRIOUZEAU Signed-off-by: Arturo Buzarra --- ...nk-match-drm-kernel-driver-alignment.patch | 45 ++++++++++++++++++ ...dsink-match-drm-kernel-driver-alignm.patch | 46 +++++++++++++++++++ .../gstreamer1.0-plugins-bad_1.22.12.bbappend | 2 + 3 files changed, 93 insertions(+) create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0082-Revert-waylandsink-match-drm-kernel-driver-alignment.patch create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0083-Revert-gtkwaylandsink-match-drm-kernel-driver-alignm.patch diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0082-Revert-waylandsink-match-drm-kernel-driver-alignment.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0082-Revert-waylandsink-match-drm-kernel-driver-alignment.patch new file mode 100644 index 000000000..4fff06632 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0082-Revert-waylandsink-match-drm-kernel-driver-alignment.patch @@ -0,0 +1,45 @@ +From be6728ba8e66dfc2f50d1af21eb0579e785765bb Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Tue, 3 Dec 2024 14:37:29 +0100 +Subject: [PATCH 1/2] Revert "waylandsink: match drm kernel driver alignment" + +This reverts commit 9468d8cb21818d7fcdbc569119f6056806673cc3. + +Upstream-Status: Pending +--- + ext/wayland/gstwaylandsink.c | 12 ------------ + 1 file changed, 12 deletions(-) + +diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c +index 066f05e..c93fec9 100644 +--- a/ext/wayland/gstwaylandsink.c ++++ b/ext/wayland/gstwaylandsink.c +@@ -692,8 +692,6 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + GstWaylandSink *self = GST_WAYLAND_SINK (bsink);; + gboolean use_dmabuf; + GstVideoFormat format; +- GstVideoAlignment alignment; +- guint width; + + GST_DEBUG_OBJECT (self, "set caps %" GST_PTR_FORMAT, caps); + +@@ -701,16 +699,6 @@ gst_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + if (!gst_video_info_from_caps (&self->video_info, caps)) + goto invalid_format; + +- /* +- * stick to DRM kernel driver alignment (128 bytes) to avoid +- * any board effects due to size mismatch when switching +- * later-on with DRM dumb allocated buffers +- */ +- gst_video_alignment_reset (&alignment); +- width = GST_VIDEO_INFO_WIDTH (&self->video_info); +- alignment.padding_right = GST_ROUND_UP_128 (width) - width; +- gst_video_info_align (&self->video_info, &alignment); +- + format = GST_VIDEO_INFO_FORMAT (&self->video_info); + self->video_info_changed = TRUE; + self->skip_dumb_buffer_copy = FALSE; +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0083-Revert-gtkwaylandsink-match-drm-kernel-driver-alignm.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0083-Revert-gtkwaylandsink-match-drm-kernel-driver-alignm.patch new file mode 100644 index 000000000..5017bd496 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0083-Revert-gtkwaylandsink-match-drm-kernel-driver-alignm.patch @@ -0,0 +1,46 @@ +From 658760663e957a688b9e4a7791438556e464dcdc Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Wed, 4 Dec 2024 09:15:11 +0100 +Subject: [PATCH 2/2] Revert "gtkwaylandsink: match drm kernel driver + alignment" + +This reverts commit cc4d700ec4e9737974cd2945119b100dd075b8e3. + +Upstream-Status: Pending +--- + ext/gtk/gstgtkwaylandsink.c | 12 ------------ + 1 file changed, 12 deletions(-) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index fbe05be..658effd 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -949,8 +949,6 @@ gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + gst_gtk_wayland_sink_get_instance_private (self); + gboolean use_dmabuf; + GstVideoFormat format; +- GstVideoAlignment alignment; +- guint width; + + GST_DEBUG_OBJECT (self, "set caps %" GST_PTR_FORMAT, caps); + +@@ -958,16 +956,6 @@ gst_gtk_wayland_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) + if (!gst_video_info_from_caps (&priv->video_info, caps)) + goto invalid_format; + +- /* +- * stick to DRM kernel driver alignment (128 bytes) to avoid +- * any board effects due to size mismatch when switching +- * later-on with DRM dumb allocated buffers +- */ +- gst_video_alignment_reset (&alignment); +- width = GST_VIDEO_INFO_WIDTH (&priv->video_info); +- alignment.padding_right = GST_ROUND_UP_128 (width) - width; +- gst_video_info_align (&priv->video_info, &alignment); +- + format = GST_VIDEO_INFO_FORMAT (&priv->video_info); + priv->video_info_changed = TRUE; + priv->skip_dumb_buffer_copy = FALSE; +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend index b160980e7..137040930 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend @@ -76,6 +76,8 @@ SRC_URI:append = " \ file://0079-waylandsink-config-buffer-pool-with-query-size-when-.patch \ file://0080-gtkwaylandsink-config-buffer-pool-with-query-size-wh.patch \ file://0081-v4l2codecs-add-support-of-encoding-from-RGBA.patch \ + file://0082-Revert-waylandsink-match-drm-kernel-driver-alignment.patch \ + file://0083-Revert-gtkwaylandsink-match-drm-kernel-driver-alignm.patch \ " PACKAGECONFIG_GL ?= "${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'gles2 egl', '', d)}" From 0d10d91ed25227c4d0c2799c2ca617ee100c001b Mon Sep 17 00:00:00 2001 From: Christophe Priouzeau Date: Wed, 12 Feb 2025 09:28:58 +0100 Subject: [PATCH 036/138] stm-st-stm32mp: gstreamer-plugins-bad: v4l2codecs: support H264 encode high profile DCT 8x8 Support H264 encode high profile DCT 8x8. gst-launch-1.0 videotestsrc num-buffers=100 ! videoconvert ! v4l2slh264enc dct8x8=true ! "video/x-h264, profile=(string)high" ! h264parse ! qtmux ! filesink location=qvga_high_dct8x8.mp4 gst-play-1.0 qvga_high_dct8x8.mp4 gst-launch-1.0 videotestsrc num-buffers=100 ! videoconvert ! v4l2slh264enc dct8x8=true ! "video/x-h264, profile=(string)high" ! filesink location=qvga_high_dct8x8.bits gst-play-1.0 qvga_high_dct8x8.bits https://onedigi.atlassian.net/browse/DEL-9546 Change-Id: I63287f10b252d0f82b94a7c48807b5ba4a25e359 Signed-off-by: Hugues Fruchet Reviewed-on: https://gerrit.st.com/c/oeivi/oe/st/meta-opensdk/+/436380 Reviewed-by: Christophe PRIOUZEAU Tested-by: Christophe PRIOUZEAU ACI: CITOOLS Signed-off-by: Arturo Buzarra --- ...odecs-h264enc-add-support-of-DCT-8x8.patch | 147 ++++++++++++++++++ .../gstreamer1.0-plugins-bad_1.22.12.bbappend | 1 + 2 files changed, 148 insertions(+) create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0084-v4l2codecs-h264enc-add-support-of-DCT-8x8.patch diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0084-v4l2codecs-h264enc-add-support-of-DCT-8x8.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0084-v4l2codecs-h264enc-add-support-of-DCT-8x8.patch new file mode 100644 index 000000000..cb5faaa22 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0084-v4l2codecs-h264enc-add-support-of-DCT-8x8.patch @@ -0,0 +1,147 @@ +From a379e9e5b378c75b5d22302266729830b8095bd3 Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Thu, 12 Dec 2024 15:21:45 +0100 +Subject: [PATCH] v4l2codecs: h264enc: add support of DCT 8x8 + +Add support of DCT 8x8, this tool is part of high profile. + +gst-launch-1.0 videotestsrc num-buffers=100 ! videoconvert ! v4l2slh264enc dct8x8=true ! "video/x-h264, profile=(string)high" ! h264parse ! qtmux ! filesink location=qvga_high_dct8x8.mp4 +gst-play-1.0 qvga_high_dct8x8.mp4 + +gst-launch-1.0 videotestsrc num-buffers=100 ! videoconvert ! v4l2slh264enc dct8x8=true ! "video/x-h264, profile=(string)high" ! filesink location=qvga_high_dct8x8.bits +gst-play-1.0 qvga_high_dct8x8.bits + +Upstream-Status: Pending +--- + gst-libs/gst/codecs/gsth264encoder.c | 18 ++++++++++++++++++ + sys/v4l2codecs/gstv4l2codech264enc.c | 22 +++++++++++++++++++++- + 2 files changed, 39 insertions(+), 1 deletion(-) + +diff --git a/gst-libs/gst/codecs/gsth264encoder.c b/gst-libs/gst/codecs/gsth264encoder.c +index f1b0200..7dc4fb8 100644 +--- a/gst-libs/gst/codecs/gsth264encoder.c ++++ b/gst-libs/gst/codecs/gsth264encoder.c +@@ -51,6 +51,7 @@ enum + PROP_BITRATE, + PROP_CABAC, + PROP_CABAC_INIT_IDC, ++ PROP_DCT8X8, + PROP_RATE_CONTROL, + }; + +@@ -63,6 +64,7 @@ struct _GstH264EncoderPrivate + gint keyframe_interval; + gboolean cabac; + guint cabac_init_idc; ++ gboolean dct8x8; + }; + + #define parent_class gst_h264_encoder_parent_class +@@ -224,6 +226,9 @@ gst_h264_encoder_get_property (GObject * object, guint property_id, + case PROP_CABAC_INIT_IDC: + g_value_set_uint (value, priv->cabac_init_idc); + break; ++ case PROP_DCT8X8: ++ g_value_set_boolean (value, priv->dct8x8); ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; +@@ -269,6 +274,9 @@ gst_h264_encoder_set_property (GObject * object, guint property_id, + case PROP_CABAC_INIT_IDC: + priv->cabac_init_idc = g_value_get_uint (value); + break; ++ case PROP_DCT8X8: ++ priv->dct8x8 = g_value_get_boolean (value); ++ break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; +@@ -381,6 +389,16 @@ gst_h264_encoder_class_init (GstH264EncoderClass * klass) + 0, 2, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT)); + ++ /** ++ * GstH264Encoder:dct8x8: ++ * Note: Supported only on high profile ++ * ++ * Since: 1.2x ++ */ ++ g_object_class_install_property (object_class, PROP_DCT8X8, ++ g_param_spec_boolean ("dct8x8", "DCT8X8", ++ "Adaptive spatial transform size", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ + /** + * GstH264Encoder:rate-control: + * +diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c +index 921302a..eac5300 100644 +--- a/sys/v4l2codecs/gstv4l2codech264enc.c ++++ b/sys/v4l2codecs/gstv4l2codech264enc.c +@@ -56,7 +56,7 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ], " + "stream-format = (string) byte-stream, " + "alignment = (string) au, " +- "profile = (string) { main, constrained-baseline, baseline}") ++ "profile = (string) { high, main, constrained-baseline, baseline}") + ); + + /* Maximum sizes for common headers (in bits) */ +@@ -84,6 +84,7 @@ struct _GstV4l2CodecH264Enc + gint qp_init, qp_max, qp_min; + gboolean cabac; + guint cabac_init_idc; ++ gboolean dct8x8; + + gchar *profile_name; + guint level_idc; +@@ -543,6 +544,7 @@ gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self, + self->pps.second_chroma_qp_index_offset = self->pps.chroma_qp_index_offset; + self->pps.deblocking_filter_control_present_flag = 1; + self->pps.entropy_coding_mode_flag = self->cabac; ++ self->pps.transform_8x8_mode_flag = self->dct8x8; + } + + /* Begin of code taken from VA plugin */ +@@ -600,6 +602,9 @@ gst_v4l2_codec_h264_enc_decide_profile_and_level (GstV4l2CodecH264Enc * self, + g_object_get (self, "cabac", &self->cabac, "cabac-init-idc", + &self->cabac_init_idc, NULL); + ++ g_object_get (self, "dct8x8", &self->dct8x8, "dct8x8", ++ &self->dct8x8, NULL); ++ + /* First, check whether the downstream requires a specified profile. */ + allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (self)); + if (!allowed_caps) +@@ -620,6 +625,15 @@ gst_v4l2_codec_h264_enc_decide_profile_and_level (GstV4l2CodecH264Enc * self, + } + } + ++ if (self->dct8x8) { ++ if (!g_strstr_len (profile_name, -1, "high")) { ++ GST_WARNING_OBJECT (self, ++ "DCT 8x8 is not supported by user selected profile '%s'" ++ ", disabling this features", profile_name); ++ self->dct8x8 = FALSE; ++ } ++ } ++ + g_free (self->profile_name); + self->profile_name = g_strdup (profile_name); + +@@ -975,6 +989,12 @@ gst_v4l2_codec_h264_enc_fill_encode_params (GstH264Encoder * encoder, + self->encode_params.flags &= ~V4L2_H264_ENCODE_FLAG_ENTROPY_CABAC; + } + ++ if (self->dct8x8) { ++ self->encode_params.flags |= V4L2_H264_ENCODE_FLAG_TRANSFORM_8X8_MODE; ++ } else { ++ self->encode_params.flags &= ~V4L2_H264_ENCODE_FLAG_TRANSFORM_8X8_MODE; ++ } ++ + self->encode_params.pic_parameter_set_id = 0; + self->encode_params.cabac_init_idc = self->cabac_init_idc; + +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend index 137040930..780d60073 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend @@ -78,6 +78,7 @@ SRC_URI:append = " \ file://0081-v4l2codecs-add-support-of-encoding-from-RGBA.patch \ file://0082-Revert-waylandsink-match-drm-kernel-driver-alignment.patch \ file://0083-Revert-gtkwaylandsink-match-drm-kernel-driver-alignm.patch \ + file://0084-v4l2codecs-h264enc-add-support-of-DCT-8x8.patch \ " PACKAGECONFIG_GL ?= "${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'gles2 egl', '', d)}" From a4d8a1a9850b7682a6c350e51ad0f71cb0c77293 Mon Sep 17 00:00:00 2001 From: Christophe Priouzeau Date: Wed, 12 Feb 2025 09:29:22 +0100 Subject: [PATCH 037/138] stm-st-stm32mp: gstreamer-plugins-bad: v4l2codecs: add support of encoding from UYVY Add UYVY support in order to be aligned with stateful v4l2 codecs such as v4l2jpegenc. https://onedigi.atlassian.net/browse/DEL-9546 Change-Id: I8c8e0b328aa3ea2325085d26cad0c14549b25f5a Reviewed-on: https://gerrit.st.com/c/oeivi/oe/st/meta-opensdk/+/436381 ACI: CITOOLS Tested-by: Christophe PRIOUZEAU Reviewed-by: Christophe PRIOUZEAU Signed-off-by: Arturo Buzarra --- ...cs-add-support-of-encoding-from-UYVY.patch | 41 +++++++++++++++++++ .../gstreamer1.0-plugins-bad_1.22.12.bbappend | 1 + 2 files changed, 42 insertions(+) create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0085-v4l2codecs-add-support-of-encoding-from-UYVY.patch diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0085-v4l2codecs-add-support-of-encoding-from-UYVY.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0085-v4l2codecs-add-support-of-encoding-from-UYVY.patch new file mode 100644 index 000000000..3d5c48b3e --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0085-v4l2codecs-add-support-of-encoding-from-UYVY.patch @@ -0,0 +1,41 @@ +From 0a93b2551d40fd6d4b4d222f552d329ea17524db Mon Sep 17 00:00:00 2001 +From: Hugues Fruchet +Date: Wed, 29 Jan 2025 15:43:14 +0100 +Subject: [PATCH] v4l2codecs: add support of encoding from UYVY + +Add support of encoding from UYVY + +Upstream-Status: Pending +--- + sys/v4l2codecs/gstv4l2format.c | 1 + + sys/v4l2codecs/gstv4l2format.h | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/sys/v4l2codecs/gstv4l2format.c b/sys/v4l2codecs/gstv4l2format.c +index 2d930d1..57b3e9a 100644 +--- a/sys/v4l2codecs/gstv4l2format.c ++++ b/sys/v4l2codecs/gstv4l2format.c +@@ -35,6 +35,7 @@ static struct FormatEntry format_map[] = { + {V4L2_PIX_FMT_NV12M, 2, GST_VIDEO_FORMAT_NV12, 8, 420}, + {V4L2_PIX_FMT_NV12, 1, GST_VIDEO_FORMAT_NV12, 8, 420}, + {V4L2_PIX_FMT_YUYV, 1, GST_VIDEO_FORMAT_YUY2, 8, 422}, ++ {V4L2_PIX_FMT_UYVY, 1, GST_VIDEO_FORMAT_UYVY, 8, 422}, + {V4L2_PIX_FMT_SUNXI_TILED_NV12, 1, GST_VIDEO_FORMAT_NV12_32L32, 8, 422}, + {V4L2_PIX_FMT_NV12_4L4, 1, GST_VIDEO_FORMAT_NV12_4L4, 8, 420}, + {V4L2_PIX_FMT_MM21, 2, GST_VIDEO_FORMAT_NV12_16L32S, 8, 420}, +diff --git a/sys/v4l2codecs/gstv4l2format.h b/sys/v4l2codecs/gstv4l2format.h +index 2bf675f..c0c2ccd 100644 +--- a/sys/v4l2codecs/gstv4l2format.h ++++ b/sys/v4l2codecs/gstv4l2format.h +@@ -24,7 +24,7 @@ + #include + #include "linux/videodev2.h" + +-#define GST_V4L2_DEFAULT_VIDEO_FORMATS "{ NV12, YUY2, NV12_4L4, NV12_32L32, NV12_16L32S, I420, P010_10LE, RGB16, BGRx, BGRA, RGBx, RGBA, xBGR, ABGR, xRGB, ARGB}" ++#define GST_V4L2_DEFAULT_VIDEO_FORMATS "{ NV12, YUY2, UYVY, NV12_4L4, NV12_32L32, NV12_16L32S, I420, P010_10LE, RGB16, BGRx, BGRA, RGBx, RGBA, xBGR, ABGR, xRGB, ARGB}" + + gboolean gst_v4l2_format_to_video_info (struct v4l2_format * fmt, + GstVideoInfo * out_info); +-- +2.25.1 + diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend index 780d60073..95a873c4e 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend @@ -79,6 +79,7 @@ SRC_URI:append = " \ file://0082-Revert-waylandsink-match-drm-kernel-driver-alignment.patch \ file://0083-Revert-gtkwaylandsink-match-drm-kernel-driver-alignm.patch \ file://0084-v4l2codecs-h264enc-add-support-of-DCT-8x8.patch \ + file://0085-v4l2codecs-add-support-of-encoding-from-UYVY.patch \ " PACKAGECONFIG_GL ?= "${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'gles2 egl', '', d)}" From c2c933a93af06c6e9c02919630247d65f832173b Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Thu, 27 Mar 2025 17:02:54 +0100 Subject: [PATCH 038/138] imx-atf,imx-boot: refresh ccimx8m patches for release lf-6.6.52-2.2.0 https://onedigi.atlassian.net/browse/DEL-9537 https://onedigi.atlassian.net/browse/DEL-9541 Signed-off-by: Javier Viguera --- ...fine-UART1-as-console-for-boot-stage.patch | 4 +- ...fine-UART1-as-console-for-boot-stage.patch | 4 +- ...8m-soc.mak-preserve-dtbs-after-build.patch | 48 ------------------ ...pture-commands-output-into-a-log-fil.patch | 12 ++--- ...-imx8m-print_fit_hab-follow-symlinks.patch | 49 +++++++++++++++++++ .../imx-mkimage/imx-boot_1.0.bbappend | 2 +- 6 files changed, 59 insertions(+), 60 deletions(-) delete mode 100644 meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/files/0001-imx8m-soc.mak-preserve-dtbs-after-build.patch create mode 100644 meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/files/0003-imx8m-print_fit_hab-follow-symlinks.patch diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-atf/imx-atf/0001-imx8mm-Define-UART1-as-console-for-boot-stage.patch b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-atf/imx-atf/0001-imx8mm-Define-UART1-as-console-for-boot-stage.patch index 708ec7821..a65a1d6c4 100644 --- a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-atf/imx-atf/0001-imx8mm-Define-UART1-as-console-for-boot-stage.patch +++ b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-atf/imx-atf/0001-imx8mm-Define-UART1-as-console-for-boot-stage.patch @@ -13,10 +13,10 @@ Signed-off-by: Gabriel Valcazar 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plat/imx/imx8m/imx8mm/platform.mk b/plat/imx/imx8m/imx8mm/platform.mk -index 0a8f12a26..55f83f462 100644 +index 41fc3507f..1de30f40f 100644 --- a/plat/imx/imx8m/imx8mm/platform.mk +++ b/plat/imx/imx8m/imx8mm/platform.mk -@@ -159,7 +159,7 @@ $(eval $(call add_define,BL32_BASE)) +@@ -172,7 +172,7 @@ $(eval $(call add_define,BL32_BASE)) BL32_SIZE ?= 0x2000000 $(eval $(call add_define,BL32_SIZE)) diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-atf/imx-atf/0003-imx8mn-Define-UART1-as-console-for-boot-stage.patch b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-atf/imx-atf/0003-imx8mn-Define-UART1-as-console-for-boot-stage.patch index dddee0012..ca6f033ae 100644 --- a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-atf/imx-atf/0003-imx8mn-Define-UART1-as-console-for-boot-stage.patch +++ b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-atf/imx-atf/0003-imx8mn-Define-UART1-as-console-for-boot-stage.patch @@ -27,10 +27,10 @@ index 312eb32d1..9a82be010 100644 /* memory region */ diff --git a/plat/imx/imx8m/imx8mn/platform.mk b/plat/imx/imx8m/imx8mn/platform.mk -index da4e40aa3..0e38cfa0b 100644 +index 84519426c..964ed9e5c 100644 --- a/plat/imx/imx8m/imx8mn/platform.mk +++ b/plat/imx/imx8m/imx8mn/platform.mk -@@ -65,7 +65,7 @@ $(eval $(call add_define,BL32_BASE)) +@@ -77,7 +77,7 @@ $(eval $(call add_define,BL32_BASE)) BL32_SIZE ?= 0x2000000 $(eval $(call add_define,BL32_SIZE)) diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/files/0001-imx8m-soc.mak-preserve-dtbs-after-build.patch b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/files/0001-imx8m-soc.mak-preserve-dtbs-after-build.patch deleted file mode 100644 index c65c7859d..000000000 --- a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/files/0001-imx8m-soc.mak-preserve-dtbs-after-build.patch +++ /dev/null @@ -1,48 +0,0 @@ -From: Gonzalo Ruiz -Date: Wed, 23 Sep 2020 10:15:08 +0200 -Subject: [PATCH] imx8m: soc.mak: preserve dtbs after build - -On commit ab433440269bbd5383b47ee60957e9906cda0b3b a dtb preprocess -was introduced to have compatibility with old U-Boots. - -As part of those changes, the content of variable $(dtbs) was deleted -at the end of the build. - -Since we added the dtbs variable as an argument, building is causing the -original dtb file to be removed, and a second build is failing since the -file pointed at by the dtbs variable does no longer exist. - -Remove the file removal to avoid this. - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Gonzalo Ruiz -Signed-off-by: Gabriel Valcazar -Signed-off-by: Javier Viguera ---- - iMX8M/soc.mak | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/iMX8M/soc.mak b/iMX8M/soc.mak -index 6751759..5a96614 100644 ---- a/iMX8M/soc.mak -+++ b/iMX8M/soc.mak -@@ -161,7 +161,7 @@ u-boot.itb: $(dtb) $(supp_dtbs) - ./$(PAD_IMAGE) u-boot-nodtb.bin $(dtb) $(supp_dtbs) - BL32=$(TEE) DEK_BLOB_LOAD_ADDR=$(DEK_BLOB_LOAD_ADDR) TEE_LOAD_ADDR=$(TEE_LOAD_ADDR) ATF_LOAD_ADDR=$(ATF_LOAD_ADDR) ../$(SOC_DIR)/mkimage_fit_atf.sh $(dtb) $(supp_dtbs) > u-boot.its - mkimage -E -p $(FIT_EXTERNAL_POSITION) -f u-boot.its u-boot.itb -- @rm -f u-boot.its $(dtb) -+ @rm -f u-boot.its - - dtb_ddr3l = valddr3l.dtb - $(dtb_ddr3l): -@@ -329,7 +329,8 @@ print_fit_hab: u-boot-nodtb.bin bl31.bin $(dtb) $(supp_dtbs) - ./$(PAD_IMAGE) bl31.bin - ./$(PAD_IMAGE) u-boot-nodtb.bin $(dtb) $(supp_dtbs) - FIT_DATA_POS=$(FIT_EXTERNAL_POSITION) TEE_LOAD_ADDR=$(TEE_LOAD_ADDR) ATF_LOAD_ADDR=$(ATF_LOAD_ADDR) VERSION=$(VERSION) ../$(SOC_DIR)/print_fit_hab.sh $(PRINT_FIT_HAB_OFFSET) $(dtb) $(supp_dtbs) -- @rm -f $(dtb) -+ ./$(PAD_IMAGE) u-boot-nodtb.bin $(dtb) -+ TEE_LOAD_ADDR=$(TEE_LOAD_ADDR) ATF_LOAD_ADDR=$(ATF_LOAD_ADDR) VERSION=$(VERSION) ../$(SOC_DIR)/print_fit_hab.sh $(PRINT_FIT_HAB_OFFSET) $(dtb) - - print_fit_hab_ddr4: u-boot-nodtb.bin bl31.bin $(dtb_ddr4_evk) $(supp_dtbs) - ./$(PAD_IMAGE) $(TEE) diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/files/0002-imx8m-soc.mak-capture-commands-output-into-a-log-fil.patch b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/files/0002-imx8m-soc.mak-capture-commands-output-into-a-log-fil.patch index 0f58f0c42..c519ee55f 100644 --- a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/files/0002-imx8m-soc.mak-capture-commands-output-into-a-log-fil.patch +++ b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/files/0002-imx8m-soc.mak-capture-commands-output-into-a-log-fil.patch @@ -13,7 +13,7 @@ Signed-off-by: Javier Viguera 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/iMX8M/soc.mak b/iMX8M/soc.mak -index 5a96614..5901d5e 100644 +index f1b37a4f425a..ffd70af8ac7d 100644 --- a/iMX8M/soc.mak +++ b/iMX8M/soc.mak @@ -1,5 +1,6 @@ @@ -32,7 +32,7 @@ index 5a96614..5901d5e 100644 flash_evk_no_hdmi_dual_bootloader: $(MKIMG) u-boot-spl-ddr.bin u-boot.itb ./mkimage_imx8 -version $(VERSION) -fit -loader u-boot-spl-ddr.bin $(SPL_LOAD_ADDR) -out $(OUTIMG) -@@ -325,12 +326,12 @@ flash_dp_spl_uboot: flash_dp_evk +@@ -325,10 +326,10 @@ flash_dp_spl_uboot: flash_dp_evk flash_spl_uboot: flash_evk_no_hdmi print_fit_hab: u-boot-nodtb.bin bl31.bin $(dtb) $(supp_dtbs) @@ -40,10 +40,8 @@ index 5a96614..5901d5e 100644 + ./$(PAD_IMAGE) $(TEE) 2>&1 | tee $(MKIMAGE_LOG) ./$(PAD_IMAGE) bl31.bin ./$(PAD_IMAGE) u-boot-nodtb.bin $(dtb) $(supp_dtbs) - FIT_DATA_POS=$(FIT_EXTERNAL_POSITION) TEE_LOAD_ADDR=$(TEE_LOAD_ADDR) ATF_LOAD_ADDR=$(ATF_LOAD_ADDR) VERSION=$(VERSION) ../$(SOC_DIR)/print_fit_hab.sh $(PRINT_FIT_HAB_OFFSET) $(dtb) $(supp_dtbs) - ./$(PAD_IMAGE) u-boot-nodtb.bin $(dtb) -- TEE_LOAD_ADDR=$(TEE_LOAD_ADDR) ATF_LOAD_ADDR=$(ATF_LOAD_ADDR) VERSION=$(VERSION) ../$(SOC_DIR)/print_fit_hab.sh $(PRINT_FIT_HAB_OFFSET) $(dtb) -+ { echo $@; TEE_LOAD_ADDR=$(TEE_LOAD_ADDR) ATF_LOAD_ADDR=$(ATF_LOAD_ADDR) VERSION=$(VERSION) ../$(SOC_DIR)/print_fit_hab.sh $(PRINT_FIT_HAB_OFFSET) $(dtb) 2>&1; } | tee -a $(MKIMAGE_LOG) +- FIT_DATA_POS=$(FIT_EXTERNAL_POSITION) TEE_LOAD_ADDR=$(TEE_LOAD_ADDR) ATF_LOAD_ADDR=$(ATF_LOAD_ADDR) VERSION=$(VERSION) ../$(SOC_DIR)/print_fit_hab.sh $(PRINT_FIT_HAB_OFFSET) $(dtb) $(supp_dtbs) ++ { echo $@; FIT_DATA_POS=$(FIT_EXTERNAL_POSITION) TEE_LOAD_ADDR=$(TEE_LOAD_ADDR) ATF_LOAD_ADDR=$(ATF_LOAD_ADDR) VERSION=$(VERSION) ../$(SOC_DIR)/print_fit_hab.sh $(PRINT_FIT_HAB_OFFSET) $(dtb) $(supp_dtbs) 2>&1; } | tee -a $(MKIMAGE_LOG) + @rm -f $(dtb) print_fit_hab_ddr4: u-boot-nodtb.bin bl31.bin $(dtb_ddr4_evk) $(supp_dtbs) - ./$(PAD_IMAGE) $(TEE) diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/files/0003-imx8m-print_fit_hab-follow-symlinks.patch b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/files/0003-imx8m-print_fit_hab-follow-symlinks.patch new file mode 100644 index 000000000..7f3a2c7f2 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/files/0003-imx8m-print_fit_hab-follow-symlinks.patch @@ -0,0 +1,49 @@ +From: Javier Viguera +Date: Tue, 5 Dec 2023 18:10:43 +0100 +Subject: [PATCH] imx8m: print_fit_hab: follow symlinks + +Upstream-Status: Inappropriate [DEY specific] + +Signed-off-by: Javier Viguera +--- + iMX8M/print_fit_hab.sh | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/iMX8M/print_fit_hab.sh b/iMX8M/print_fit_hab.sh +index 06fca5f88554..a65f78c07a0f 100755 +--- a/iMX8M/print_fit_hab.sh ++++ b/iMX8M/print_fit_hab.sh +@@ -33,7 +33,7 @@ else + let uboot_sign_off=$((fit_off - 0x8000 - ivt_off + $FIT_DATA_POS)) + fi + +-let uboot_size=$(stat --printf="%s" $BL33) ++let uboot_size=$(stat -L --printf="%s" $BL33) + let uboot_load_addr=0x40200000 + + let last_sign_off=$(((uboot_sign_off + uboot_size + 3) & ~3)) +@@ -50,7 +50,7 @@ for dtname in $* + do + if [ ${cnt} != 0 ] + then +- let fdt${cnt}_size=$(stat --printf="%s" $dtname) ++ let fdt${cnt}_size=$(stat -L --printf="%s" $dtname) + + let fdt${cnt}_sign_off=$((last_sign_off)) + let fdt${cnt}_load_addr=$((last_load_addr)) +@@ -71,13 +71,13 @@ done + + let atf_sign_off=$((last_sign_off)) + let atf_load_addr=$ATF_LOAD_ADDR +-let atf_size=$(stat --printf="%s" $BL31) ++let atf_size=$(stat -L --printf="%s" $BL31) + + if [ ! -f $BL32 ]; then + let tee_size=0x0 + let tee_sign_off=$((atf_sign_off + atf_size)) + else +- let tee_size=$(stat --printf="%s" $BL32) ++ let tee_size=$(stat -L --printf="%s" $BL32) + + let tee_sign_off=$(((atf_sign_off + atf_size + 3) & ~3)) + let tee_load_addr=$TEE_LOAD_ADDR diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/imx-boot_1.0.bbappend b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/imx-boot_1.0.bbappend index e31bfd85d..67b8e2981 100644 --- a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/imx-boot_1.0.bbappend +++ b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/imx-boot_1.0.bbappend @@ -3,8 +3,8 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/files:" SRC_URI:append:ccimx8m = " \ - file://0001-imx8m-soc.mak-preserve-dtbs-after-build.patch \ file://0002-imx8m-soc.mak-capture-commands-output-into-a-log-fil.patch \ + file://0003-imx8m-print_fit_hab-follow-symlinks.patch \ " SRC_URI:append:ccimx8x = " \ file://0001-iMX8QX-soc.mak-capture-commands-output-into-a-log-fi.patch \ From 907531b55cb6d421db4454bba5105b71071f6502 Mon Sep 17 00:00:00 2001 From: Gabriel Valcazar Date: Fri, 4 Apr 2025 10:11:07 +0200 Subject: [PATCH 039/138] kernel-module-nxp-wlan: remove wlan_src_driver_patch_release_lf-6.6.52-2.2.0 The patch was recently added to meta-freescale, remove it from our layer to avoid build errors. Signed-off-by: Gabriel Valcazar --- ...rc_driver_patch_release_lf-6.6.52-2..patch | 1943 ----------------- .../kernel-module-nxp-wlan_git.bbappend | 1 - 2 files changed, 1944 deletions(-) delete mode 100644 meta-digi-arm/dynamic-layers/freescale-layer/recipes-kernel/kernel-modules/kernel-module-nxp-wlan/0001-issue-fix-wlan_src_driver_patch_release_lf-6.6.52-2..patch diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-kernel/kernel-modules/kernel-module-nxp-wlan/0001-issue-fix-wlan_src_driver_patch_release_lf-6.6.52-2..patch b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-kernel/kernel-modules/kernel-module-nxp-wlan/0001-issue-fix-wlan_src_driver_patch_release_lf-6.6.52-2..patch deleted file mode 100644 index adb6f7c9a..000000000 --- a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-kernel/kernel-modules/kernel-module-nxp-wlan/0001-issue-fix-wlan_src_driver_patch_release_lf-6.6.52-2..patch +++ /dev/null @@ -1,1943 +0,0 @@ -From: Gonzalo Ruiz -Date: Mon, 10 Feb 2025 09:49:59 +0100 -Subject: [PATCH 1/1] issue fix: wlan_src_driver_patch_release_lf-6.6.52-2.2.0 - -Patch from Wireless_Patch_Release_IW612_W8997_W8801_v0.1. -Incremental patch release based on the Q4-2024 release -"lf-6.6.52-2.2.0". - -[Patch Description] - - Wi-Fi driver patch to fix the issues observed on version 505.p4. - -Signed-off-by: Gonzalo Ruiz ---- - Makefile | 4 +- - mlan/mlan_cmdevt.c | 13 +++- - mlan/mlan_decl.h | 3 +- - mlan/mlan_fw.h | 8 +- - mlan/mlan_join.c | 8 +- - mlan/mlan_main.h | 3 +- - mlan/mlan_pcie.c | 10 ++- - mlan/mlan_shim.c | 3 +- - mlan/mlan_sta_cmd.c | 14 +++- - mlan/mlan_sta_cmdresp.c | 9 ++- - mlan/mlan_uap_cmdevent.c | 3 +- - mlan/mlan_wmm.c | 35 +++++---- - mlinux/mlan_decl.h | 3 +- - mlinux/moal_cfg80211.c | 47 +++++------- - mlinux/moal_cfg80211.h | 32 ++++---- - mlinux/moal_init.c | 30 +++++++- - mlinux/moal_ioctl.c | 11 ++- - mlinux/moal_main.c | 152 ++++++++++++++++++++++++++++++++----- - mlinux/moal_main.h | 8 +- - mlinux/moal_pcie.c | 10 ++- - mlinux/moal_sdio_mmc.c | 65 +++++++++++++--- - mlinux/moal_shim.c | 47 +++--------- - mlinux/moal_sta_cfg80211.c | 92 +++++++++++++--------- - mlinux/moal_uap_cfg80211.c | 58 +++++--------- - 24 files changed, 445 insertions(+), 223 deletions(-) - -diff --git a/Makefile b/Makefile -index 024e977..d61798e 100644 ---- a/Makefile -+++ b/Makefile -@@ -1,6 +1,6 @@ - # File: Makefile - # --# Copyright 2008-2024 NXP -+# Copyright 2008-2025 NXP - # - # This software file (the File) is distributed by NXP - # under the terms of the GNU General Public License Version 2, June 1991 -@@ -188,7 +188,7 @@ APPDIR= $(shell if test -d "mapp"; then echo mapp; fi) - ############################################################################# - - ccflags-y += -I$(KERNELDIR)/include -- ccflags-y += -DMLAN_RELEASE_VERSION='"505.p4"' -+ ccflags-y += -DMLAN_RELEASE_VERSION='"505.p7.1"' - - ccflags-y += -DFPNUM='"92"' - -diff --git a/mlan/mlan_cmdevt.c b/mlan/mlan_cmdevt.c -index 49c645e..dc0c8ba 100644 ---- a/mlan/mlan_cmdevt.c -+++ b/mlan/mlan_cmdevt.c -@@ -4,7 +4,7 @@ - * @brief This file contains the handling of CMD/EVENT in MLAN - * - * -- * Copyright 2009-2024 NXP -+ * Copyright 2009-2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -5272,6 +5272,17 @@ mlan_status wlan_adapter_init_cmd(pmlan_adapter pmadapter) - } - } - #endif -+ if (pmpriv && (pmadapter->init_para.disable_11h_tpc)) { -+ /* Send command to FW to disable 11h tpc */ -+ ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_SNMP_MIB, -+ HostCmd_ACT_GEN_SET, -+ Dot11h_disable_tpc_i, MNULL, -+ &pmadapter->init_para.disable_11h_tpc); -+ if (ret) { -+ ret = MLAN_STATUS_FAILURE; -+ goto done; -+ } -+ } - #ifdef STA_SUPPORT - if (pmpriv_sta && (pmadapter->ps_mode == Wlan802_11PowerModePSP)) { - ret = wlan_prepare_cmd(pmpriv_sta, -diff --git a/mlan/mlan_decl.h b/mlan/mlan_decl.h -index d1367d6..97bc08f 100644 ---- a/mlan/mlan_decl.h -+++ b/mlan/mlan_decl.h -@@ -3,7 +3,7 @@ - * @brief This file declares the generic data structures and APIs. - * - * -- * Copyright 2008-2022, 2024 NXP -+ * Copyright 2008-2022, 2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -2782,6 +2782,7 @@ typedef struct _mlan_device { - t_u32 max_tx_pending; - t_u16 tx_budget; - t_u8 mclient_scheduling; -+ t_u8 disable_11h_tpc; - } mlan_device, *pmlan_device; - - /** MLAN API function prototype */ -diff --git a/mlan/mlan_fw.h b/mlan/mlan_fw.h -index 6ce0451..df8efa6 100644 ---- a/mlan/mlan_fw.h -+++ b/mlan/mlan_fw.h -@@ -5,7 +5,7 @@ - * in MLAN module. - * - * -- * Copyright 2008-2024 NXP -+ * Copyright 2008-2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -1674,11 +1674,14 @@ typedef MLAN_PACK_START struct _RxPD { - t_u32 rx_info; - - /** Reserved */ -- t_u8 reserved3[8]; -+ t_u8 reserved3[7]; -+ t_u8 rx_pkt_flags; - t_u8 ta_mac[6]; - t_u8 reserved4[2]; - } MLAN_PACK_END RxPD, *PRxPD; - -+#define RX_PKT_FLAG_MEF_MATCH_PKT MBIT(0) -+ - /** IEEEtypes_FrameCtl_t*/ - #ifdef BIG_ENDIAN_SUPPORT - typedef MLAN_PACK_START struct _IEEEtypes_FrameCtl_t { -@@ -3686,6 +3689,7 @@ typedef enum _SNMP_MIB_INDEX { - StopDeauth_i = 44, - Dot11H_fakeRadar = 45, - ChanTrackParam_i = 46, -+ Dot11h_disable_tpc_i = 47, - } SNMP_MIB_INDEX; - - /** max SNMP buf size */ -diff --git a/mlan/mlan_join.c b/mlan/mlan_join.c -index f4c8860..34fe4e7 100644 ---- a/mlan/mlan_join.c -+++ b/mlan/mlan_join.c -@@ -7,7 +7,7 @@ - * to the firmware. - * - * -- * Copyright 2008-2024 NXP -+ * Copyright 2008-2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -1046,6 +1046,7 @@ mlan_status wlan_cmd_802_11_associate(mlan_private *pmpriv, - t_u32 rates_size; - t_u16 tmp_cap; - t_u8 *pos; -+ IEEEtypes_CapInfo_t *pcap_info; - t_u8 ft_akm = 0; - t_u8 oper_class; - t_u8 oper_class_flag = MFALSE; -@@ -1524,6 +1525,11 @@ mlan_status wlan_cmd_802_11_associate(mlan_private *pmpriv, - memcpy_ext(pmadapter, &tmp_cap, &pbss_desc->cap_info, - sizeof(passo->cap_info), sizeof(tmp_cap)); - -+ /* retain spectrum_mgmt capability */ -+ pcap_info = &passo->cap_info; -+ if (pcap_info->spectrum_mgmt) -+ SPECTRUM_MGMT_ENABLED(tmp_cap); -+ - if (pmpriv->config_bands == BAND_B) - SHORT_SLOT_TIME_DISABLED(tmp_cap); - -diff --git a/mlan/mlan_main.h b/mlan/mlan_main.h -index 2e41d37..258bd1f 100644 ---- a/mlan/mlan_main.h -+++ b/mlan/mlan_main.h -@@ -5,7 +5,7 @@ - * in MLAN module. - * - * -- * Copyright 2008-2024 NXP -+ * Copyright 2008-2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -2042,6 +2042,7 @@ typedef struct _mlan_init_para { - t_u16 tx_budget; - t_u8 mclient_scheduling; - t_u32 reject_addba_req; -+ t_u8 disable_11h_tpc; - } mlan_init_para, *pmlan_init_para; - - #ifdef SDIO -diff --git a/mlan/mlan_pcie.c b/mlan/mlan_pcie.c -index 3575a9a..c29fb6e 100644 ---- a/mlan/mlan_pcie.c -+++ b/mlan/mlan_pcie.c -@@ -3,7 +3,7 @@ - * @brief This file contains PCI-E specific code - * - * -- * Copyright 2008-2021, 2024 NXP -+ * Copyright 2008-2021, 2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -2134,6 +2134,8 @@ static mlan_status wlan_pcie_send_data_complete(mlan_adapter *pmadapter) - ptx_bd_buf->flags = 0; - ptx_bd_buf->frag_len = 0; - ptx_bd_buf->offset = 0; -+ pcb->moal_spin_lock(pmadapter->pmoal_handle, -+ pmadapter->pmlan_pcie_lock); - pmadapter->pcard_pcie->txbd_rdptr++; - if ((pmadapter->pcard_pcie->txbd_rdptr & - txrx_rw_ptr_mask) == num_tx_buffs) -@@ -2141,6 +2143,8 @@ static mlan_status wlan_pcie_send_data_complete(mlan_adapter *pmadapter) - ((pmadapter->pcard_pcie->txbd_rdptr & - txrx_rw_ptr_rollover_ind) ^ - txrx_rw_ptr_rollover_ind); -+ pcb->moal_spin_unlock(pmadapter->pmoal_handle, -+ pmadapter->pmlan_pcie_lock); - } - #endif - #if defined(PCIE9098) || defined(PCIE9097) || defined(PCIEAW693) || \ -@@ -2154,9 +2158,13 @@ static mlan_status wlan_pcie_send_data_complete(mlan_adapter *pmadapter) - padma_bd_buf->flags = 0; - padma_bd_buf->pkt_size = 0; - padma_bd_buf->reserved = 0; -+ pcb->moal_spin_lock(pmadapter->pmoal_handle, -+ pmadapter->pmlan_pcie_lock); - pmadapter->pcard_pcie->txbd_rdptr++; - pmadapter->pcard_pcie->txbd_rdptr &= - ADMA_RW_PTR_WRAP_MASK; -+ pcb->moal_spin_unlock(pmadapter->pmoal_handle, -+ pmadapter->pmlan_pcie_lock); - } - #endif - } -diff --git a/mlan/mlan_shim.c b/mlan/mlan_shim.c -index 0752d7f..d27ab7e 100644 ---- a/mlan/mlan_shim.c -+++ b/mlan/mlan_shim.c -@@ -3,7 +3,7 @@ - * @brief This file contains APIs to MOAL module. - * - * -- * Copyright 2008-2021, 2024 NXP -+ * Copyright 2008-2021, 2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -474,6 +474,7 @@ mlan_status mlan_register(pmlan_device pmdevice, t_void **ppmlan_adapter) - #endif - pmadapter->init_para.dfs53cfg = pmdevice->dfs53cfg; - pmadapter->init_para.dfs_offload = pmdevice->dfs_offload; -+ pmadapter->init_para.disable_11h_tpc = pmdevice->disable_11h_tpc; - pmadapter->priv_num = 0; - pmadapter->priv[0] = MNULL; - -diff --git a/mlan/mlan_sta_cmd.c b/mlan/mlan_sta_cmd.c -index 4dd1712..2e16779 100644 ---- a/mlan/mlan_sta_cmd.c -+++ b/mlan/mlan_sta_cmd.c -@@ -5,7 +5,7 @@ - * it is ready. - * - * -- * Copyright 2008-2024 NXP -+ * Copyright 2008-2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -242,6 +242,18 @@ static mlan_status wlan_cmd_802_11_snmp_mib(pmlan_private pmpriv, - cmd->size += sizeof(t_u16); - } - break; -+ case Dot11h_disable_tpc_i: -+ psnmp_mib->oid = wlan_cpu_to_le16((t_u16)Dot11h_disable_tpc_i); -+ if (cmd_action == HostCmd_ACT_GEN_SET) { -+ psnmp_mib->query_type = -+ wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); -+ psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u16)); -+ ul_temp = *(t_u32 *)pdata_buf; -+ *((t_u16 *)(psnmp_mib->value)) = -+ wlan_cpu_to_le16((t_u16)ul_temp); -+ cmd->size += sizeof(t_u16); -+ } -+ break; - case WwsMode_i: - psnmp_mib->oid = wlan_cpu_to_le16((t_u16)WwsMode_i); - if (cmd_action == HostCmd_ACT_GEN_SET) { -diff --git a/mlan/mlan_sta_cmdresp.c b/mlan/mlan_sta_cmdresp.c -index 116506c..f88f91d 100644 ---- a/mlan/mlan_sta_cmdresp.c -+++ b/mlan/mlan_sta_cmdresp.c -@@ -4,7 +4,7 @@ - * responses generated by firmware. - * - * -- * Copyright 2008-2024 NXP -+ * Copyright 2008-2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -643,6 +643,13 @@ static mlan_status wlan_ret_802_11_snmp_mib(pmlan_private pmpriv, - (ul_temp & SLAVE_RADAR_DET_MASK) ? MTRUE : - MFALSE; - } -+ /* Update state for 11h tpc disable */ -+ if (oid == Dot11h_disable_tpc_i) { -+ /* Set 11h tpc to private */ -+ ul_temp = wlan_le16_to_cpu(*((t_u16 *)(psmib->value))); -+ PRINTM(MCMND, "SNMP_RESP: Dot11h_disable_tpc_i =%u\n", -+ ul_temp); -+ } - } - - if (pioctl_buf) { -diff --git a/mlan/mlan_uap_cmdevent.c b/mlan/mlan_uap_cmdevent.c -index 6c50db9..66c01c5 100644 ---- a/mlan/mlan_uap_cmdevent.c -+++ b/mlan/mlan_uap_cmdevent.c -@@ -3,7 +3,7 @@ - * @brief This file contains the handling of AP mode command and event - * - * -- * Copyright 2009-2024 NXP -+ * Copyright 2009-2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -3024,6 +3024,7 @@ static mlan_status wlan_uap_cmd_snmp_mib(pmlan_private pmpriv, - switch (cmd_oid) { - case Dot11D_i: - case Dot11H_i: -+ case Dot11h_disable_tpc_i: - psnmp_mib->oid = wlan_cpu_to_le16((t_u16)cmd_oid); - psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u16)); - ul_temp = *(t_u32 *)pdata_buf; -diff --git a/mlan/mlan_wmm.c b/mlan/mlan_wmm.c -index 9a8083a..6da49ea 100644 ---- a/mlan/mlan_wmm.c -+++ b/mlan/mlan_wmm.c -@@ -3,7 +3,7 @@ - * @brief This file contains functions for WMM. - * - * -- * Copyright 2008-2021, 2024 NXP -+ * Copyright 2008-2021, 2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -840,11 +840,11 @@ static raListTbl *wlan_wmm_get_highest_priolist_ptr(pmlan_adapter pmadapter, - * - * @return byte budget - */ --static t_u32 wlan_wmm_get_byte_budget(pmlan_adapter pmadapter, t_u32 time_budget_us, t_u32 phy_rate_kbps) -+static t_u32 wlan_wmm_get_byte_budget(t_u32 time_budget_us, t_u32 phy_rate_kbps) - { - const t_u32 min_budget = MV_ETH_FRAME_LEN; -- t_u64 byte_budget = pmadapter->callbacks.moal_do_div((t_u64)phy_rate_kbps * time_budget_us, -- 8 * 1000u); -+ t_u64 byte_budget = -+ ((t_u64)phy_rate_kbps * time_budget_us) / (8 * 1000u); - - if (byte_budget > INT_MAX) - return INT_MAX; -@@ -891,7 +891,7 @@ wlan_wmm_allocate_sta_table(pmlan_adapter pmadapter, t_u8 *ra) - - sta_table->budget.time_budget_init_us = pmadapter->init_para.tx_budget; - sta_table->budget.byte_budget_init = wlan_wmm_get_byte_budget( -- pmadapter, sta_table->budget.time_budget_init_us, default_rate); -+ sta_table->budget.time_budget_init_us, default_rate); - sta_table->budget.queue_packets = default_queue_packets; - sta_table->budget.phy_rate_kbps = default_rate; - -@@ -900,14 +900,14 @@ wlan_wmm_allocate_sta_table(pmlan_adapter pmadapter, t_u8 *ra) - sta_table->budget.mpdu_no_amsdu_pps_cap = - pmadapter->tx_mpdu_no_amsdu_pps; - -- sta_table->budget.mpdu_with_amsdu_budget_init = pmadapter->callbacks.moal_do_div( -- (t_u64)sta_table->budget.mpdu_with_amsdu_pps_cap * -- sta_table->budget.time_budget_init_us, -- 1000000); -- sta_table->budget.mpdu_no_amsdu_budget_init = pmadapter->callbacks.moal_do_div( -- (t_u64)sta_table->budget.mpdu_no_amsdu_pps_cap * -- sta_table->budget.time_budget_init_us, -- 1000000); -+ sta_table->budget.mpdu_with_amsdu_budget_init = -+ ((t_u64)sta_table->budget.mpdu_with_amsdu_pps_cap * -+ sta_table->budget.time_budget_init_us) / -+ 1000000; -+ sta_table->budget.mpdu_no_amsdu_budget_init = -+ ((t_u64)sta_table->budget.mpdu_no_amsdu_pps_cap * -+ sta_table->budget.time_budget_init_us) / -+ 1000000; - - for (i = 0; i < NELEMENTS(sta_table->budget.bytes); ++i) { - sta_table->budget.bytes[i] = sta_table->budget.byte_budget_init; -@@ -3142,12 +3142,12 @@ static t_void wlan_wmm_update_queue_packets_budget(pmlan_adapter pmadapter, - list_entry, struct wmm_sta_table, active_sta_entry); - const t_u64 sta_capacity = sta->budget.byte_budget_init; - const t_u32 max_pkts_by_airtime = -- wlan_wmm_get_byte_budget(pmadapter, max_pending_tx_time_us, -+ wlan_wmm_get_byte_budget(max_pending_tx_time_us, - sta->budget.phy_rate_kbps) / - MV_ETH_FRAME_LEN; -- t_u32 sta_share = pmadapter->callbacks.moal_do_div((t_u64)queue_packets_limit * sta_capacity, -- total_capacity); - -+ t_u32 sta_share = -+ queue_packets_limit * sta_capacity / total_capacity; - sta_share = MAX(sta_share, min_sta_share); - sta_share = MIN(sta_share, queue_packets_limit * 7 / 8); - sta_share = MIN(sta_share, max_pkts_by_airtime); -@@ -5183,7 +5183,6 @@ static void wlan_wmm_adjust_sta_tx_budget(pmlan_private priv, - struct wmm_sta_table *sta, - HostCmd_TX_RATE_QUERY *rate) - { -- mlan_adapter *pmadapter = priv->adapter; - const t_u8 ppdu_type_legacy = 0; - const t_u8 ppdu_type_ht = 1; - const t_u8 ppdu_type_vht = 2; -@@ -5211,7 +5210,7 @@ static void wlan_wmm_adjust_sta_tx_budget(pmlan_private priv, - if (phy_rate > 0) { - const t_u32 old_phy_rate = sta->budget.phy_rate_kbps; - sta->budget.byte_budget_init = wlan_wmm_get_byte_budget( -- pmadapter, sta->budget.time_budget_init_us, phy_rate); -+ sta->budget.time_budget_init_us, phy_rate); - sta->budget.phy_rate_kbps = phy_rate; - - if (old_phy_rate / phy_rate >= 2 || -diff --git a/mlinux/mlan_decl.h b/mlinux/mlan_decl.h -index d1367d6..97bc08f 100644 ---- a/mlinux/mlan_decl.h -+++ b/mlinux/mlan_decl.h -@@ -3,7 +3,7 @@ - * @brief This file declares the generic data structures and APIs. - * - * -- * Copyright 2008-2022, 2024 NXP -+ * Copyright 2008-2022, 2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -2782,6 +2782,7 @@ typedef struct _mlan_device { - t_u32 max_tx_pending; - t_u16 tx_budget; - t_u8 mclient_scheduling; -+ t_u8 disable_11h_tpc; - } mlan_device, *pmlan_device; - - /** MLAN API function prototype */ -diff --git a/mlinux/moal_cfg80211.c b/mlinux/moal_cfg80211.c -index 074fd59..0e54436 100644 ---- a/mlinux/moal_cfg80211.c -+++ b/mlinux/moal_cfg80211.c -@@ -3,7 +3,7 @@ - * @brief This file contains the functions for CFG80211. - * - * -- * Copyright 2011-2024 NXP -+ * Copyright 2011-2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -1105,10 +1105,7 @@ void woal_cancel_cac(moal_private *priv) - if (woal_11h_cancel_chan_report_ioctl(priv, MOAL_IOCTL_WAIT)) - PRINTM(MERROR, "%s: cancel chan report failed \n", - __func__); --#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) -- cfg80211_cac_event(priv->netdev, &priv->phandle->dfs_channel, -- NL80211_RADAR_CAC_ABORTED, GFP_KERNEL, 0); --#elif CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) -+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) - cfg80211_cac_event(priv->netdev, &priv->phandle->dfs_channel, - NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); - #else -@@ -1203,12 +1200,7 @@ int woal_cfg80211_change_virtual_intf(struct wiphy *wiphy, - PRINTM(MERROR, - "%s: cancel chan report failed \n", - __func__); --#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) -- cfg80211_cac_event(priv->netdev, -- &priv->phandle->dfs_channel, -- NL80211_RADAR_CAC_ABORTED, -- GFP_KERNEL, 0); --#elif CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) -+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) - cfg80211_cac_event(priv->netdev, - &priv->phandle->dfs_channel, - NL80211_RADAR_CAC_ABORTED, -@@ -1322,8 +1314,8 @@ int woal_cfg80211_change_virtual_intf(struct wiphy *wiphy, - #endif /* WIFI_DIRECT_SUPPORT */ - #if defined(STA_SUPPORT) && defined(UAP_SUPPORT) - if (priv->bss_type == MLAN_BSS_TYPE_UAP) { --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - woal_cfg80211_del_beacon(wiphy, dev, 0); - #else - woal_cfg80211_del_beacon(wiphy, dev); -@@ -1562,7 +1554,8 @@ fail: - */ - #endif - int woal_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, --#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || IMX_ANDROID_13) -+#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 33)) - int link_id, - #endif - t_u8 key_index, -@@ -1638,7 +1631,8 @@ int woal_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, - */ - #endif - int woal_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, --#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || IMX_ANDROID_13) -+#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 33)) - int link_id, - #endif - t_u8 key_index, -@@ -1655,13 +1649,9 @@ int woal_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, - LEAVE(); - return -EFAULT; - } -- /* del_key will be trigger from cfg80211_rx_mlme_mgmt funtion -- * where we receive deauth/disassoicate packet in rx_work -- * use MOAL_NO_WAIT to avoid dead lock -- */ - if (MLAN_STATUS_FAILURE == - woal_cfg80211_set_key(priv, 0, 0, NULL, 0, NULL, 0, key_index, -- mac_addr, 1, 0, MOAL_NO_WAIT)) { -+ mac_addr, 1, 0, MOAL_IOCTL_WAIT)) { - PRINTM(MERROR, "Error deleting the crypto keys\n"); - LEAVE(); - return -EFAULT; -@@ -1697,7 +1687,8 @@ int woal_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, - #endif - int woal_cfg80211_set_default_key(struct wiphy *wiphy, - struct net_device *netdev, --#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || IMX_ANDROID_13) -+#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 33)) - int link_id, - #endif - t_u8 key_index -@@ -1736,7 +1727,8 @@ int woal_cfg80211_set_default_key(struct wiphy *wiphy, - #if KERNEL_VERSION(2, 6, 30) <= CFG80211_VERSION_CODE - int woal_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, - struct net_device *netdev, --#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || IMX_ANDROID_13) -+#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 33)) - int link_id, - #endif - t_u8 key_index) -@@ -1750,7 +1742,8 @@ int woal_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, - #if KERNEL_VERSION(5, 10, 0) <= CFG80211_VERSION_CODE - int woal_cfg80211_set_default_beacon_key(struct wiphy *wiphy, - struct net_device *netdev, --#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || IMX_ANDROID_13) -+#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 33)) - int link_id, - #endif - t_u8 key_index) -@@ -2337,8 +2330,8 @@ done: - * @return 0 -- success, otherwise fail - */ - int woal_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev, --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - unsigned int link_id, - #endif - const u8 *peer, -@@ -5484,11 +5477,11 @@ void woal_cfg80211_notify_channel(moal_private *priv, - CFG80211_VERSION_CODE < KERNEL_VERSION(6, 9, 0) - cfg80211_ch_switch_notify(priv->netdev, &chandef, 0, 0); - #elif ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) && \ -- IMX_ANDROID_13)) && \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 33))) && \ - CFG80211_VERSION_CODE < KERNEL_VERSION(6, 9, 0) - cfg80211_ch_switch_notify(priv->netdev, &chandef, 0, 0); - #elif ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -- IMX_ANDROID_13 || IMX_ANDROID_12_BACKPORT) -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - cfg80211_ch_switch_notify(priv->netdev, &chandef, 0); - #else - cfg80211_ch_switch_notify(priv->netdev, &chandef); -diff --git a/mlinux/moal_cfg80211.h b/mlinux/moal_cfg80211.h -index 3323055..1b8484b 100644 ---- a/mlinux/moal_cfg80211.h -+++ b/mlinux/moal_cfg80211.h -@@ -3,7 +3,7 @@ - * @brief This file contains the CFG80211 specific defines. - * - * -- * Copyright 2011-2022, 2024 NXP -+ * Copyright 2011-2022, 2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -133,7 +133,8 @@ int woal_cfg80211_change_virtual_intf(struct wiphy *wiphy, - int woal_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed); - - int woal_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, --#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || IMX_ANDROID_13) -+#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 33)) - int link_id, - #endif - t_u8 key_index, -@@ -143,7 +144,8 @@ int woal_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, - const t_u8 *mac_addr, struct key_params *params); - - int woal_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, --#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || IMX_ANDROID_13) -+#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 33)) - int link_id, - #endif - t_u8 key_index, -@@ -167,8 +169,8 @@ int woal_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev); - #endif - - int woal_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev, --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - unsigned int link_id, - #endif - const u8 *peer, -@@ -219,7 +221,8 @@ int woal_cfg80211_set_channel(struct wiphy *wiphy, - - #if KERNEL_VERSION(2, 6, 37) < CFG80211_VERSION_CODE - int woal_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *dev, --#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || IMX_ANDROID_13) -+#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 33)) - int link_id, - #endif - t_u8 key_index, bool ucast, bool mcast); -@@ -231,7 +234,8 @@ int woal_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *dev, - #if KERNEL_VERSION(2, 6, 30) <= CFG80211_VERSION_CODE - int woal_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, - struct net_device *netdev, --#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || IMX_ANDROID_13) -+#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 33)) - int link_id, - #endif - t_u8 key_index); -@@ -240,7 +244,8 @@ int woal_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, - #if KERNEL_VERSION(5, 10, 0) <= CFG80211_VERSION_CODE - int woal_cfg80211_set_default_beacon_key(struct wiphy *wiphy, - struct net_device *netdev, --#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || IMX_ANDROID_13) -+#if ((KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 33)) - int link_id, - #endif - t_u8 key_index); -@@ -453,8 +458,8 @@ int woal_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *params); - #endif - --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - int woal_cfg80211_del_beacon(struct wiphy *wiphy, struct net_device *dev, - unsigned int link_id); - #else -@@ -472,12 +477,7 @@ int woal_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, - #endif - - #if KERNEL_VERSION(3, 12, 0) <= CFG80211_VERSION_CODE --#if KERNEL_VERSION(6, 12, 0) <= CFG80211_VERSION_CODE --int woal_cfg80211_start_radar_detection(struct wiphy *wiphy, -- struct net_device *dev, -- struct cfg80211_chan_def *chandef, -- u32 cac_time_msi, int link_id); --#elif KERNEL_VERSION(3, 15, 0) <= CFG80211_VERSION_CODE -+#if KERNEL_VERSION(3, 15, 0) <= CFG80211_VERSION_CODE - int woal_cfg80211_start_radar_detection(struct wiphy *wiphy, - struct net_device *dev, - struct cfg80211_chan_def *chandef, -diff --git a/mlinux/moal_init.c b/mlinux/moal_init.c -index f790666..845d272 100644 ---- a/mlinux/moal_init.c -+++ b/mlinux/moal_init.c -@@ -4,7 +4,7 @@ - * driver. - * - * -- * Copyright 2018-2022, 2024 NXP -+ * Copyright 2018-2022, 2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -336,6 +336,9 @@ static int mon_filter = DEFAULT_NETMON_FILTER; - - int dual_nb; - -+/** disable 802.11h tpc configuration */ -+static int disable_11h_tpc = 0; -+ - #ifdef DEBUG_LEVEL1 - #ifdef DEBUG_LEVEL2 - #define DEFAULT_DEBUG_MASK (0xffffffff) -@@ -1597,6 +1600,14 @@ static mlan_status parse_cfg_read_block(t_u8 *data, t_u32 size, - params->reject_addba_req = out_data; - PRINTM(MMSG, "reject_addba_req=%x\n", - params->reject_addba_req); -+ } else if (strncmp(line, "disable_11h_tpc", -+ strlen("disable_11h_tpc")) == 0) { -+ if (parse_line_read_int(line, &out_data) != -+ MLAN_STATUS_SUCCESS) -+ goto err; -+ params->disable_11h_tpc = out_data; -+ PRINTM(MMSG, "disable_11h_tpc=%x\n", -+ params->disable_11h_tpc); - } - } - -@@ -2000,6 +2011,13 @@ static void woal_setup_module_param(moal_handle *handle, moal_mod_para *params) - handle->params.dual_nb = dual_nb; - if (params) - handle->params.dual_nb = params->dual_nb; -+ -+ handle->params.disable_11h_tpc = disable_11h_tpc; -+ /* Ignore country IE when 11h tpc is disabled */ -+ if (disable_11h_tpc) -+ moal_extflg_set(handle, EXT_COUNTRY_IE_IGNORE); -+ if (params) -+ handle->params.disable_11h_tpc = params->disable_11h_tpc; - } - - /** -@@ -2568,6 +2586,12 @@ void woal_init_from_dev_tree(void) - data); - reject_addba_req = data; - } -+ } else if (!strncmp(prop->name, "disable_11h_tpc", -+ strlen("disable_11h_tpc"))) { -+ if (!of_property_read_u32(dt_node, prop->name, &data)) { -+ PRINTM(MERROR, "disable_11h_tpc=0x%x\n", data); -+ disable_11h_tpc = data; -+ } - } - #if defined(STA_CFG80211) || defined(UAP_CFG80211) - #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) -@@ -3187,3 +3211,7 @@ module_param(reject_addba_req, int, 0); - MODULE_PARM_DESC( - reject_addba_req, - "Bit1: Reject the addba request when FW auto re-connect enabled (STA BSS only); Bit0: Reject the addba request when HS activated"); -+ -+module_param(disable_11h_tpc, int, 0); -+MODULE_PARM_DESC(disable_11h_tpc, -+ "0: Enable 802.11h tpc; 1: Disable 802.11h tpc"); -diff --git a/mlinux/moal_ioctl.c b/mlinux/moal_ioctl.c -index 94b8b2a..49711ea 100644 ---- a/mlinux/moal_ioctl.c -+++ b/mlinux/moal_ioctl.c -@@ -3,7 +3,7 @@ - * @brief This file contains ioctl function to MLAN - * - * -- * Copyright 2008-2024 NXP -+ * Copyright 2008-2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -6101,13 +6101,19 @@ mlan_status woal_cancel_scan(moal_private *priv, t_u8 wait_option) - #ifdef STA_CFG80211 - unsigned long flags; - #endif -+ -+#ifdef STA_CFG80211 -+ // cancel scan timeout -+ if (IS_STA_CFG80211(handle->params.cfg80211_wext) && -+ handle->scan_request) -+ cancel_delayed_work(&handle->scan_timeout_work); -+#endif - /* If scan is in process, cancel the scan command */ - if (!handle->scan_pending_on_block || !scan_priv) { - #ifdef STA_CFG80211 - spin_lock_irqsave(&handle->scan_req_lock, flags); - if (IS_STA_CFG80211(handle->params.cfg80211_wext) && - handle->scan_request) { -- cancel_delayed_work(&handle->scan_timeout_work); - /* some supplicant cannot handle SCAN abort event */ - if (scan_priv && - (scan_priv->bss_type == MLAN_BSS_TYPE_STA)) -@@ -6138,7 +6144,6 @@ mlan_status woal_cancel_scan(moal_private *priv, t_u8 wait_option) - spin_lock_irqsave(&handle->scan_req_lock, flags); - if (IS_STA_CFG80211(handle->params.cfg80211_wext) && - handle->scan_request) { -- cancel_delayed_work(&handle->scan_timeout_work); - /** some supplicant can not handle SCAN abort event */ - if (scan_priv->bss_type == MLAN_BSS_TYPE_STA) - woal_cfg80211_scan_done(handle->scan_request, MTRUE); -diff --git a/mlinux/moal_main.c b/mlinux/moal_main.c -index e7f3242..a478a2c 100644 ---- a/mlinux/moal_main.c -+++ b/mlinux/moal_main.c -@@ -4,7 +4,7 @@ - * driver. - * - * -- * Copyright 2008-2024 NXP -+ * Copyright 2008-2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -174,6 +174,7 @@ static struct _card_info card_info_SD8887 = { - .fw_reset_val = 1, - .fw_wakeup_reg = 0, - .fw_wakeup_val = 2, -+ .fw_winner_status_reg = 0x90, - .slew_rate_reg = 0x80002328, - .slew_rate_bit_offset = 12, - #endif -@@ -214,6 +215,7 @@ static struct _card_info card_info_SD8897 = { - .fw_reset_val = 1, - .fw_wakeup_reg = 0, - .fw_wakeup_val = 2, -+ .fw_winner_status_reg = 0xC0, - .slew_rate_reg = 0x80002328, - .slew_rate_bit_offset = 12, - #endif -@@ -299,6 +301,7 @@ static struct _card_info card_info_SD8977 = { - .fw_reset_val = 0x99, - .fw_wakeup_reg = 0, - .fw_wakeup_val = 2, -+ .fw_winner_status_reg = 0xFC, - .slew_rate_reg = 0x80002328, - .slew_rate_bit_offset = 12, - #endif -@@ -342,6 +345,7 @@ static struct _card_info card_info_SD8978 = { - .fw_reset_val = 0x99, - .fw_wakeup_reg = 0, - .fw_wakeup_val = 2, -+ .fw_winner_status_reg = 0xFC, - .slew_rate_reg = 0x80002328, - .slew_rate_bit_offset = 12, - #endif -@@ -385,6 +389,7 @@ static struct _card_info card_info_SD8997 = { - .fw_reset_val = 0x99, - .fw_wakeup_reg = 0, - .fw_wakeup_val = 2, -+ .fw_winner_status_reg = 0xFC, - .slew_rate_reg = 0x80002328, - .slew_rate_bit_offset = 12, - #endif -@@ -429,6 +434,7 @@ static struct _card_info card_info_SD9098 = { - .fw_reset_val = 0x99, - .fw_wakeup_reg = 0, - .fw_wakeup_val = 2, -+ .fw_winner_status_reg = 0xFC, - .slew_rate_reg = 0x90002328, - .slew_rate_bit_offset = 12, - #endif -@@ -473,6 +479,7 @@ static struct _card_info card_info_SD9097 = { - .fw_reset_val = 0x99, - .fw_wakeup_reg = 0, - .fw_wakeup_val = 2, -+ .fw_winner_status_reg = 0xFC, - .slew_rate_reg = 0x90002328, - .slew_rate_bit_offset = 12, - #endif -@@ -517,6 +524,7 @@ static struct _card_info card_info_SDIW624 = { - .fw_reset_val = 0x99, - .fw_wakeup_reg = 0, - .fw_wakeup_val = 2, -+ .fw_winner_status_reg = 0xFC, - .slew_rate_reg = 0x90002328, - .slew_rate_bit_offset = 12, - #endif -@@ -561,6 +569,7 @@ static struct _card_info card_info_SDAW693 = { - .fw_reset_val = 0x99, - .fw_wakeup_reg = 0, - .fw_wakeup_val = 2, -+ .fw_winner_status_reg = 0xFC, - .slew_rate_reg = 0x90002328, - .slew_rate_bit_offset = 12, - #endif -@@ -605,6 +614,7 @@ static struct _card_info card_info_SD9177 = { - .fw_reset_val = 0x99, - .fw_wakeup_reg = 0, - .fw_wakeup_val = 2, -+ .fw_winner_status_reg = 0xFC, - .slew_rate_reg = 0x90002328, - .slew_rate_bit_offset = 12, - #endif -@@ -649,6 +659,7 @@ static struct _card_info card_info_SDIW610 = { - .fw_reset_val = 0x99, - .fw_wakeup_reg = 0, - .fw_wakeup_val = 2, -+ .fw_winner_status_reg = 0xFC, - .slew_rate_reg = 0x45001064, - .slew_rate_bit_offset = 16, - #endif -@@ -976,6 +987,7 @@ static struct _card_info card_info_SD8987 = { - .fw_reset_val = 0x99, - .fw_wakeup_reg = 0, - .fw_wakeup_val = 2, -+ .fw_winner_status_reg = 0xFC, - .slew_rate_reg = 0x80002328, - .slew_rate_bit_offset = 12, - #endif -@@ -989,8 +1001,7 @@ static struct _card_info card_info_SD8987 = { - - /** Driver version */ - char driver_version[] = -- INTF_CARDTYPE KERN_VERSION "--" MLAN_RELEASE_VERSION "-GPL" -- "-(" -+ INTF_CARDTYPE KERN_VERSION "--" MLAN_RELEASE_VERSION "-(" - "FP" FPNUM ")" - #ifdef DEBUG_LEVEL2 - "-dbg" -@@ -1224,11 +1235,7 @@ void woal_clean_up(moal_handle *handle) - handle->cac_period = MFALSE; - priv = handle->priv[handle->cac_bss_index]; - if (priv) { --#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) -- cfg80211_cac_event(priv->netdev, &handle->dfs_channel, -- NL80211_RADAR_CAC_ABORTED, -- GFP_KERNEL, 0); --#elif CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) -+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) - cfg80211_cac_event(priv->netdev, &handle->dfs_channel, - NL80211_RADAR_CAC_ABORTED, - GFP_KERNEL); -@@ -1259,8 +1266,8 @@ void woal_clean_up(moal_handle *handle) - #ifdef STA_CFG80211 - #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) - if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev && --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - priv->wdev->connected) { - #else - priv->wdev->current_bss) { -@@ -1305,6 +1312,92 @@ void woal_clean_up(moal_handle *handle) - return; - } - -+/** -+ * @brief This function send the auto recovery failure event to userspace using -+ * netlink -+ * -+ * @param handle Pointer to structure moal_handle -+ * -+ * @return N/A -+ */ -+void woal_send_auto_recovery_failure_event(moal_handle *handle) -+{ -+ mlan_status ret = MLAN_STATUS_SUCCESS; -+ struct sk_buff *skb = NULL; -+ struct nlmsghdr *nlh = NULL; -+ struct sock *sk = handle->nl_sk; -+ int len = strlen(CUS_EVT_FW_RECOVER_FAIL); -+ ENTER(); -+ -+ /* interface name to be prepended to event */ -+ /* NL_MAX_PAYLOAD = 3 * 1024 */ -+ if ((len + IFNAMSIZ) > NL_MAX_PAYLOAD) { -+ PRINTM(MERROR, "event size is too big, len=%d\n", (int)len); -+ ret = MLAN_STATUS_FAILURE; -+ goto done; -+ } -+ if (sk) { -+ /* Allocate skb */ -+ skb = alloc_skb(NLMSG_SPACE(NL_MAX_PAYLOAD), GFP_ATOMIC); -+ if (!skb) { -+ PRINTM(MERROR, "Could not allocate skb for netlink\n"); -+ ret = MLAN_STATUS_FAILURE; -+ goto done; -+ } -+ memset(skb->data, 0, NLMSG_SPACE(NL_MAX_PAYLOAD)); -+ -+ nlh = (struct nlmsghdr *)skb->data; -+ nlh->nlmsg_len = NLMSG_SPACE(len + IFNAMSIZ); -+ -+ /* From kernel */ -+ nlh->nlmsg_pid = 0; -+ nlh->nlmsg_flags = 0; -+ -+ /* Data */ -+ skb_put(skb, nlh->nlmsg_len); -+ moal_memcpy_ext(handle, NLMSG_DATA(nlh), "wlan", sizeof("wlan"), -+ nlh->nlmsg_len - NLMSG_LENGTH(0)); -+ -+ moal_memcpy_ext(handle, ((t_u8 *)(NLMSG_DATA(nlh))) + IFNAMSIZ, -+ CUS_EVT_FW_RECOVER_FAIL, len, -+ nlh->nlmsg_len - NLMSG_LENGTH(IFNAMSIZ)); -+ -+ /* From Kernel */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) -+ NETLINK_CB(skb).pid = 0; -+#else -+ NETLINK_CB(skb).portid = 0; -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) -+ /* Multicast message */ -+ NETLINK_CB(skb).dst_pid = 0; -+#endif -+ -+ /* Multicast group number */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) -+ NETLINK_CB(skb).dst_groups = NL_MULTICAST_GROUP; -+#else -+ NETLINK_CB(skb).dst_group = NL_MULTICAST_GROUP; -+#endif -+ -+ /* Send message */ -+ ret = netlink_broadcast(sk, skb, 0, NL_MULTICAST_GROUP, -+ GFP_ATOMIC); -+ if (ret) { -+ PRINTM(MWARN, "netlink_broadcast failed: ret=%d\n", -+ ret); -+ goto done; -+ } -+ -+ } else { -+ PRINTM(MERROR, -+ "Could not send event through NETLINK. Link down.\n"); -+ } -+done: -+ LEAVE(); -+} -+ - /** - * @brief This function send the auto recovery complete event to kernel - * -@@ -1412,8 +1505,8 @@ static void woal_hang_work_queue(struct work_struct *work) - #ifdef STA_CFG80211 - #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) - if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev && --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - priv->wdev->connected) { - #else - priv->wdev->current_bss) { -@@ -2603,6 +2696,7 @@ mlan_status woal_init_sw(moal_handle *handle) - handle->rtt_capa.responder_supported = MTRUE; - handle->rtt_capa.mc_version = 60; - handle->is_edmac_enabled = MFALSE; -+ handle->driver_init = MFALSE; - - /* Register to MLAN */ - memset(&device, 0, sizeof(mlan_device)); -@@ -2716,6 +2810,7 @@ mlan_status woal_init_sw(moal_handle *handle) - device.dmcs = handle->params.dmcs; - device.pref_dbc = handle->params.pref_dbc; - device.reject_addba_req = handle->params.reject_addba_req; -+ device.disable_11h_tpc = (t_u32)handle->params.disable_11h_tpc; - - for (i = 0; i < handle->drv_mode.intf_num; i++) { - device.bss_attr[i].bss_type = -@@ -4274,6 +4369,15 @@ err: - #ifdef CONFIG_PROC_FS - woal_proc_exit(handle); - #endif -+ -+#if defined(STA_CFG80211) || defined(UAP_CFG80211) -+ if (handle->wiphy) { -+ wiphy_unregister(handle->wiphy); -+ woal_cfg80211_free_bands(handle->wiphy); -+ wiphy_free(handle->wiphy); -+ handle->wiphy = NULL; -+ } -+#endif - } - LEAVE(); - return ret; -@@ -4770,6 +4874,7 @@ static mlan_status woal_request_fw_dpc(moal_handle *handle, - if (ret) - goto done; - -+ handle->driver_init = MTRUE; - done: - /* We should hold the semaphore until callback finishes execution */ - MOAL_REL_SEMAPHORE(&AddRemoveCardSem); -@@ -4910,6 +5015,9 @@ mlan_status woal_init_fw(moal_handle *handle) - ret = woal_add_card_dpc(handle); - if (ret) - goto done; -+ -+ handle->driver_init = MTRUE; -+ - /* Release semaphore if download is not required */ - MOAL_REL_SEMAPHORE(&AddRemoveCardSem); - done: -@@ -6653,8 +6761,8 @@ int woal_close(struct net_device *dev) - woal_cancel_scan(priv, MOAL_IOCTL_WAIT); - - #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev && - priv->wdev->connected) { - #else -@@ -10391,8 +10499,8 @@ t_void woal_send_disconnect_to_system(moal_private *priv, - if (IS_STA_CFG80211(cfg80211_wext)) { - spin_lock_irqsave(&priv->connect_lock, flags); - if (!priv->cfg_disconnect && !priv->cfg_connect && priv->wdev && --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - priv->wdev->connected) { - #else - priv->wdev->current_bss) { -@@ -12222,7 +12330,9 @@ t_void woal_scan_timeout_handler(struct work_struct *work) - woal_inform_bss_from_scan_result(priv, NULL, - MOAL_NO_WAIT); - spin_lock_irqsave(&handle->scan_req_lock, flags); -- woal_cfg80211_scan_done(handle->scan_request, MFALSE); -+ if (handle->scan_request) -+ woal_cfg80211_scan_done(handle->scan_request, -+ MFALSE); - handle->scan_request = NULL; - handle->fake_scan_complete = MFALSE; - spin_unlock_irqrestore(&handle->scan_req_lock, flags); -@@ -14035,6 +14145,13 @@ int woal_request_fw_reload(moal_handle *phandle, t_u8 mode) - moal_handle *ref_handle = NULL; - - ENTER(); -+ -+ if (!handle->driver_init) { -+ PRINTM(MMSG, "Ignore fw reload, driver not initialized\n"); -+ LEAVE(); -+ return -EFAULT; -+ } -+ - wifi_status = WIFI_STATUS_FW_RELOAD; - #ifdef PCIE - if (mode == FW_RELOAD_PCIE_RESET) { -@@ -14710,4 +14827,3 @@ MODULE_DESCRIPTION("M-WLAN Driver"); - MODULE_AUTHOR("NXP"); - MODULE_VERSION(MLAN_RELEASE_VERSION); - MODULE_LICENSE("GPL"); --MODULE_LICENSE("GPL"); -diff --git a/mlinux/moal_main.h b/mlinux/moal_main.h -index 6d87dcf..f8941c7 100644 ---- a/mlinux/moal_main.h -+++ b/mlinux/moal_main.h -@@ -3,7 +3,7 @@ - * @brief This file contains wlan driver specific defines etc. - * - * -- * Copyright 2008-2024 NXP -+ * Copyright 2008-2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -408,6 +408,7 @@ typedef enum _MOAL_HARDWARE_STATUS { - #define WIFI_STATUS_SCAN_TIMEOUT 8 - #define WIFI_STATUS_FW_DUMP 9 - #define WIFI_STATUS_FW_RELOAD 10 -+#define WIFI_STATUS_FW_RECOVERY_FAIL 11 - - /** fw cap info 11p */ - #define FW_CAPINFO_80211P MBIT(24) -@@ -2124,6 +2125,7 @@ typedef struct _card_info { - t_u8 func1_reg_end; - t_u32 slew_rate_reg; - t_u8 slew_rate_bit_offset; -+ t_u32 fw_winner_status_reg; - #endif - #if defined(SDIO) || defined(PCIE) - t_u32 fw_stuck_code_reg; -@@ -2724,6 +2726,8 @@ typedef struct _moal_mod_para { - int dual_nb; - /* reject addba req config for HS or FW Auto-reconnect */ - t_u32 reject_addba_req; -+ /** disable_11h_tpc setting */ -+ int disable_11h_tpc; - } moal_mod_para; - - void woal_tp_acnt_timer_func(void *context); -@@ -3265,6 +3269,7 @@ struct _moal_handle { - #endif - t_u32 ips_ctrl; - BOOLEAN is_edmac_enabled; -+ bool driver_init; - }; - - /** -@@ -4330,6 +4335,7 @@ moal_private *woal_add_interface(moal_handle *handle, t_u8 bss_num, - t_u8 bss_type); - void woal_clean_up(moal_handle *handle); - void woal_send_auto_recovery_complete_event(moal_handle *handle); -+void woal_send_auto_recovery_failure_event(moal_handle *handle); - void woal_remove_interface(moal_handle *handle, t_u8 bss_index); - void woal_set_multicast_list(struct net_device *dev); - mlan_status woal_request_fw(moal_handle *handle); -diff --git a/mlinux/moal_pcie.c b/mlinux/moal_pcie.c -index 9034124..f3e8b8c 100644 ---- a/mlinux/moal_pcie.c -+++ b/mlinux/moal_pcie.c -@@ -4,7 +4,7 @@ - * related functions. - * - * -- * Copyright 2008-2022, 2024 NXP -+ * Copyright 2008-2022, 2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -153,6 +153,8 @@ static const struct pci_device_id wlan_ids[] = { - /* moal interface ops */ - static moal_if_ops pcie_ops; - -+MODULE_DEVICE_TABLE(pci, wlan_ids); -+ - /******************************************************** - Global Variables - ********************************************************/ -@@ -2547,8 +2549,10 @@ static void woal_pcie_dump_fw_info(moal_handle *phandle) - moal_private *priv = NULL; - #ifdef DUMP_TO_PROC - if (phandle->fw_dump_buf) { -- PRINTM(MERROR, "FW dump already exist\n"); -- return; -+ PRINTM(MMSG, "FW dump already exist, free existing dump\n"); -+ moal_vfree(phandle, phandle->fw_dump_buf); -+ phandle->fw_dump_buf = NULL; -+ phandle->fw_dump_len = 0; - } - #endif - mlan_pm_wakeup_card(phandle->pmlan_adapter, MTRUE); -diff --git a/mlinux/moal_sdio_mmc.c b/mlinux/moal_sdio_mmc.c -index 299829e..44a09f0 100644 ---- a/mlinux/moal_sdio_mmc.c -+++ b/mlinux/moal_sdio_mmc.c -@@ -4,7 +4,7 @@ - * related functions. - * - * -- * Copyright 2008-2022, 2024 NXP -+ * Copyright 2008-2022, 2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -165,6 +165,8 @@ static const struct sdio_device_id wlan_ids[] = { - {}, - }; - -+MODULE_DEVICE_TABLE(sdio, wlan_ids); -+ - int woal_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id); - void woal_sdio_remove(struct sdio_func *func); - static void woal_sdiommc_work(struct work_struct *work); -@@ -281,7 +283,7 @@ static void woal_sdio_interrupt(struct sdio_func *func) - moal_handle *handle; - sdio_mmc_card *card; - mlan_status status; -- -+ t_u32 host_int_status_reg_val; - ENTER(); - - card = sdio_get_drvdata(func); -@@ -301,6 +303,14 @@ static void woal_sdio_interrupt(struct sdio_func *func) - PRINTM(MINFO, "*** IN SDIO IRQ ***\n"); - PRINTM(MINTR, "*\n"); - -+ if (handle->fw_reseting == MTRUE && (!handle->pmlan_adapter)) { -+ handle->ops.read_reg(handle, 0x0c, &host_int_status_reg_val); -+ PRINTM(MERROR, -+ "*** Recv intr during fw reset, host int status reg value is %d, ignore it ***\n", -+ host_int_status_reg_val); -+ LEAVE(); -+ return; -+ } - /* call mlan_interrupt to read int status */ - status = mlan_interrupt(0, handle->pmlan_adapter); - if (status == MLAN_STATUS_FAILURE) { -@@ -3092,8 +3102,10 @@ static void woal_sdiommc_dump_fw_info(moal_handle *phandle) - } - #ifdef DUMP_TO_PROC - if (phandle->fw_dump_buf) { -- PRINTM(MERROR, "FW dump already exist\n"); -- return; -+ PRINTM(MMSG, "FW dump already exist, free existing dump\n"); -+ moal_vfree(phandle, phandle->fw_dump_buf); -+ phandle->fw_dump_buf = NULL; -+ phandle->fw_dump_len = 0; - } - #endif - /** cancel all pending commands */ -@@ -3298,6 +3310,30 @@ void woal_sdio_reset_hw(moal_handle *handle) - return; - } - -+/** -+ * @brief This function check fw winner status -+ * -+ * @param handle A pointer to moal_handle structure -+ * -+ * @return 1--success, otherwise failure -+ */ -+static BOOLEAN woal_sdiommc_check_winner_status(moal_handle *handle) -+{ -+ t_u32 value = 1; -+ t_u32 winner_status_reg = handle->card_info->fw_winner_status_reg; -+ -+ ENTER(); -+#ifdef SD8801 -+ if (IS_SD8801(handle->card_type)) { -+ LEAVE(); -+ return MTRUE; -+ } -+#endif -+ handle->ops.read_reg(handle, winner_status_reg, &value); -+ LEAVE(); -+ return (value == 0); -+} -+ - /** - * @brief This function reload fw - * -@@ -3322,8 +3358,7 @@ static int woal_sdiommc_reset_fw(moal_handle *handle) - ret = -EFAULT; - goto done; - } -- udelay(2000); -- udelay(2000); -+ udelay(4000); - /** wait SOC fully wake up */ - for (tries = 0; tries < MAX_POLL_TRIES; ++tries) { - ret = handle->ops.write_reg(handle, reset_reg, 0xba); -@@ -3364,6 +3399,13 @@ static int woal_sdiommc_reset_fw(moal_handle *handle) - reset_reg, value); - ret = -EFAULT; - goto done; -+ } else { -+ for (tries = 0; tries < 1000; ++tries) { -+ if (woal_sdiommc_check_winner_status(handle)) { -+ break; -+ } -+ udelay(1000); -+ } - } - PRINTM(MMSG, "SDIO Trigger FW In-band Reset success"); - done: -@@ -3609,13 +3651,19 @@ static void woal_sdiommc_work(struct work_struct *work) - } - if (woal_sdiommc_reset_fw(handle)) { - PRINTM(MERROR, "SDIO In-band Reset Fail\n"); -- goto done; -+ woal_send_auto_recovery_failure_event(handle); -+ wifi_status = WIFI_STATUS_FW_RECOVERY_FAIL; -+ return; - } -+ - handle->surprise_removed = MFALSE; - if (MLAN_STATUS_SUCCESS == woal_do_sdiommc_flr(handle, false, true)) - handle->fw_reseting = MFALSE; -- else -+ else { - handle = NULL; -+ wifi_status = WIFI_STATUS_FW_RECOVERY_FAIL; -+ return; -+ } - - if (ref_handle) { - ref_handle->surprise_removed = MFALSE; -@@ -3624,7 +3672,6 @@ static void woal_sdiommc_work(struct work_struct *work) - ref_handle->fw_reseting = MFALSE; - } - card->work_flags = MFALSE; --done: - wifi_status = WIFI_STATUS_OK; - if (handle) - woal_send_auto_recovery_complete_event(handle); -diff --git a/mlinux/moal_shim.c b/mlinux/moal_shim.c -index 992e6ac..514aa95 100644 ---- a/mlinux/moal_shim.c -+++ b/mlinux/moal_shim.c -@@ -3,7 +3,7 @@ - * @brief This file contains the callback functions registered to MLAN - * - * -- * Copyright 2008-2024 NXP -+ * Copyright 2008-2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -3734,12 +3734,7 @@ mlan_status moal_recv_event(t_void *pmoal, pmlan_event pmevent) - &priv->phandle->cac_timer); - priv->phandle->is_cac_timer_set = MFALSE; - if (radar_detected) { --#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) -- cfg80211_cac_event(priv->netdev, -- &priv->phandle->dfs_channel, -- NL80211_RADAR_CAC_ABORTED, -- GFP_KERNEL, 0); --#elif CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) -+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) - cfg80211_cac_event(priv->netdev, - &priv->phandle->dfs_channel, - NL80211_RADAR_CAC_ABORTED, -@@ -3761,19 +3756,11 @@ mlan_status moal_recv_event(t_void *pmoal, pmlan_event pmevent) - // WARN_ON(!time_after_eq(jiffies, - // timeout)); mdelay(100); Using - // optimized delay --#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) -- timeout = -- (priv->wdev->links[0].cac_start_time + -- msecs_to_jiffies( -- priv->wdev -- ->links[0].cac_time_ms)); --#else - timeout = - (priv->wdev->cac_start_time + - msecs_to_jiffies( - priv->wdev - ->cac_time_ms)); --#endif - if (!time_after_eq(jiffies, timeout)) { - /* Exact time to make host and - * device timer in sync */ -@@ -3790,12 +3777,7 @@ mlan_status moal_recv_event(t_void *pmoal, pmlan_event pmevent) - } - #endif - --#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) -- cfg80211_cac_event(priv->netdev, -- &priv->phandle->dfs_channel, -- NL80211_RADAR_CAC_FINISHED, -- GFP_KERNEL, 0); --#elif CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) -+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) - cfg80211_cac_event(priv->netdev, - &priv->phandle->dfs_channel, - NL80211_RADAR_CAC_FINISHED, -@@ -3873,12 +3855,7 @@ mlan_status moal_recv_event(t_void *pmoal, pmlan_event pmevent) - woal_11h_cancel_chan_report_ioctl(priv, - MOAL_NO_WAIT); - /* upstream: inform cfg80211 */ --#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) -- cfg80211_cac_event(priv->netdev, -- &priv->phandle->dfs_channel, -- NL80211_RADAR_CAC_ABORTED, -- GFP_KERNEL, 0); --#elif CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) -+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) - cfg80211_cac_event(priv->netdev, - &priv->phandle->dfs_channel, - NL80211_RADAR_CAC_ABORTED, -@@ -3980,8 +3957,8 @@ mlan_status moal_recv_event(t_void *pmoal, pmlan_event pmevent) - || priv->uap_host_based - #endif - #ifdef STA_CFG80211 --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - || priv->wdev->connected - #else - || priv->wdev->current_bss -@@ -4132,12 +4109,12 @@ mlan_status moal_recv_event(t_void *pmoal, pmlan_event pmevent) - cfg80211_ch_switch_notify(priv->netdev, &priv->chan, 0, - 0); - #elif ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) && \ -- IMX_ANDROID_13)) && \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 33))) && \ - CFG80211_VERSION_CODE < KERNEL_VERSION(6, 9, 0) - cfg80211_ch_switch_notify(priv->netdev, &priv->chan, 0, - 0); - #elif ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -- IMX_ANDROID_13 || IMX_ANDROID_12_BACKPORT) -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - cfg80211_ch_switch_notify(priv->netdev, &priv->chan, 0); - #else - cfg80211_ch_switch_notify(priv->netdev, &priv->chan); -@@ -4486,8 +4463,8 @@ mlan_status moal_recv_event(t_void *pmoal, pmlan_event pmevent) - PRINTM(MEVENT, - "HostMlme %s: Receive deauth/disassociate\n", - priv->netdev->name); --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - if (!priv->wdev->connected) { - #else - if (!priv->wdev->current_bss) { -@@ -4919,8 +4896,8 @@ mlan_status moal_recv_event(t_void *pmoal, pmlan_event pmevent) - roam_info = - kzalloc(sizeof(struct cfg80211_roam_info), GFP_ATOMIC); - if (roam_info) { --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - roam_info->links[0].bssid = priv->cfg_bssid; - #else - roam_info->bssid = priv->cfg_bssid; -diff --git a/mlinux/moal_sta_cfg80211.c b/mlinux/moal_sta_cfg80211.c -index 3883433..f30134c 100644 ---- a/mlinux/moal_sta_cfg80211.c -+++ b/mlinux/moal_sta_cfg80211.c -@@ -3,7 +3,7 @@ - * @brief This file contains the functions for STA CFG80211. - * - * -- * Copyright 2011-2024 NXP -+ * Copyright 2011-2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -135,8 +135,8 @@ static int woal_cfg80211_dump_survey(struct wiphy *wiphy, - #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) - static int woal_cfg80211_get_channel(struct wiphy *wiphy, - struct wireless_dev *wdev, --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - unsigned int link_id, - #endif - struct cfg80211_chan_def *chandef); -@@ -2841,7 +2841,9 @@ void woal_host_mlme_process_assoc_timeout(moal_private *priv, - struct cfg80211_bss *bss) - { - /* Send Assoc Failure with Timeout to CFG80211 */ --#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) -+#if (CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 33 && \ -+ CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 15, 74))) - struct cfg80211_assoc_failure data; - memset(&data, 0, sizeof(struct cfg80211_assoc_failure)); - data.timeout = 1; -@@ -2877,7 +2879,8 @@ void woal_host_mlme_process_assoc_resp(moal_private *priv, - struct cfg80211_rx_assoc_resp_data resp = { - .uapsd_queues = -1, - }; --#elif ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#elif ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - struct cfg80211_rx_assoc_resp resp = { - .uapsd_queues = -1, - }; -@@ -2950,7 +2953,8 @@ void woal_host_mlme_process_assoc_resp(moal_private *priv, - } - } - --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - resp.links[0].bss = bss; - resp.buf = assoc_info->assoc_resp_buf; - resp.len = assoc_info->assoc_resp_len; -@@ -5021,7 +5025,9 @@ static int woal_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, - if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) - scan_req->chan_list[num_chans].scan_time = - INIT_PASSIVE_SCAN_CHAN_TIME; -- else if (priv->bss_type == MLAN_BSS_TYPE_STA) { -+ else if (priv->bss_type == MLAN_BSS_TYPE_STA && -+ scan_req->chan_list[num_chans].scan_type == -+ MLAN_SCAN_TYPE_PASSIVE) { - /* - * Set passive scan time to 110ms to discover - * all nearby AP's, Current 40ms passive scan -@@ -5034,20 +5040,17 @@ static int woal_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, - * 1. STA is in connected state - * 2. Scan type is passive - */ -- if (scan_req->chan_list[num_chans].scan_type == -- MLAN_SCAN_TYPE_PASSIVE) -- scan_req->chan_list[num_chans] -- .scan_time = -- PASSIVE_SCAN_CHAN_TIME; -- } else -+ scan_req->chan_list[num_chans].scan_time = -+ PASSIVE_SCAN_CHAN_TIME; -+ } else { - scan_req->chan_list[num_chans].scan_time = MIN( - MIN_SPECIFIC_SCAN_CHAN_TIME, - scan_cfg.scan_time.specific_scan_time); -+ } - } - #endif - #ifdef UAP_CFG80211 - if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { -- scan_req->scan_chan_gap = 0; - if (!woal_is_uap_scan_result_expired(priv)) - scan_req->chan_list[num_chans].scan_time = - MIN_SPECIFIC_SCAN_CHAN_TIME; -@@ -5850,8 +5853,8 @@ static int woal_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, - if (priv->media_connected == MFALSE) { - PRINTM(MMSG, " Already disconnected\n"); - #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - if (priv->wdev->connected && - #else - if (priv->wdev->current_bss && -@@ -6205,8 +6208,8 @@ done: - #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) - static int woal_cfg80211_get_channel(struct wiphy *wiphy, - struct wireless_dev *wdev, --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - unsigned int link_id, - #endif - struct cfg80211_chan_def *chandef) -@@ -9277,8 +9280,8 @@ int woal_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev, - passoc_rsp = (IEEEtypes_AssocRsp_t *) - assoc_rsp->assoc_resp_buf; - #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_14 || IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - roam_info.links[0].bssid = priv->cfg_bssid; - #else - roam_info.bssid = priv->cfg_bssid; -@@ -9757,8 +9760,8 @@ void woal_start_roaming(moal_private *priv) - } - #endif - #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_14 || IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - roam_info.links[0].bssid = priv->cfg_bssid; - #else - roam_info.bssid = priv->cfg_bssid; -@@ -9839,7 +9842,8 @@ int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev, - if (params->ext_capab_len) - req_len += sizeof(MrvlIEtypesHeader_t) + params->ext_capab_len; - #endif --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - if (params->link_sta_params.supported_rates_len) - req_len += sizeof(MrvlIEtypesHeader_t) + - params->link_sta_params.supported_rates_len; -@@ -9850,14 +9854,16 @@ int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev, - #endif - if (params->uapsd_queues || params->max_sp) - req_len += sizeof(MrvlIEtypesHeader_t) + sizeof(qosinfo); --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - if (params->link_sta_params.ht_capa) - #else - if (params->ht_capa) - #endif - req_len += sizeof(MrvlIEtypesHeader_t) + - sizeof(struct ieee80211_ht_cap); --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - if (params->link_sta_params.vht_capa) - #else - if (params->vht_capa) -@@ -9865,7 +9871,8 @@ int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev, - req_len += sizeof(MrvlIEtypesHeader_t) + - sizeof(struct ieee80211_vht_cap); - #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - if (params->link_sta_params.opmode_notif_used) - req_len += sizeof(MrvlIEtypesHeader_t) + sizeof(u8); - #else -@@ -9875,7 +9882,8 @@ int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev, - #endif - - #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - if (params->link_sta_params.he_capa_len) - req_len += sizeof(MrvlExtIEtypesHeader_t) + - params->link_sta_params.he_capa_len; -@@ -9931,20 +9939,23 @@ int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev, - tlv = (MrvlIEtypes_Data_t *)pos; - } - #endif --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - if (params->link_sta_params.supported_rates_len) { - #else - if (params->supported_rates_len) { - #endif - tlv = (MrvlIEtypes_Data_t *)pos; - tlv->header.type = SUPPORTED_RATES; --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - tlv->header.len = params->link_sta_params.supported_rates_len; - #else - tlv->header.len = params->supported_rates_len; - #endif - moal_memcpy_ext(priv->phandle, tlv->data, --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - params->link_sta_params.supported_rates, - tlv->header.len, - #else -@@ -9968,7 +9979,8 @@ int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev, - sizeof(MrvlIEtypesHeader_t) + tlv->header.len; - tlv = (MrvlIEtypes_Data_t *)pos; - } --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - if (params->link_sta_params.ht_capa) { - #else - if (params->ht_capa) { -@@ -9976,7 +9988,8 @@ int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev, - tlv = (MrvlIEtypes_Data_t *)pos; - tlv->header.type = HT_CAPABILITY; - tlv->header.len = sizeof(struct ieee80211_ht_cap); --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - moal_memcpy_ext(priv->phandle, tlv->data, - params->link_sta_params.ht_capa, - #else -@@ -9988,7 +10001,8 @@ int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev, - sizeof(MrvlIEtypesHeader_t) + tlv->header.len; - tlv = (MrvlIEtypes_Data_t *)pos; - } --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - if (params->link_sta_params.vht_capa) { - #else - if (params->vht_capa) { -@@ -9996,7 +10010,8 @@ int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev, - tlv = (MrvlIEtypes_Data_t *)pos; - tlv->header.type = VHT_CAPABILITY; - tlv->header.len = sizeof(struct ieee80211_vht_cap); --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - moal_memcpy_ext(priv->phandle, tlv->data, - params->link_sta_params.vht_capa, - #else -@@ -10009,7 +10024,8 @@ int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev, - tlv = (MrvlIEtypes_Data_t *)pos; - } - #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - if (params->link_sta_params.opmode_notif_used) { - #else - if (params->opmode_notif_used) { -@@ -10017,7 +10033,8 @@ int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev, - tlv = (MrvlIEtypes_Data_t *)pos; - tlv->header.type = OPER_MODE_NTF; - tlv->header.len = sizeof(u8); --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - moal_memcpy_ext(priv->phandle, tlv->data, - ¶ms->link_sta_params.opmode_notif, - #else -@@ -10030,7 +10047,8 @@ int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev, - tlv = (MrvlIEtypes_Data_t *)pos; - } - #endif --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || IMX_ANDROID_14) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 34)) - if (params->link_sta_params.he_capa_len) { - ext_tlv = (MrvlExtIEtypes_Data_t *)pos; - ext_tlv->header.type = EXTENSION; -diff --git a/mlinux/moal_uap_cfg80211.c b/mlinux/moal_uap_cfg80211.c -index 6c40a10..95aaaad 100644 ---- a/mlinux/moal_uap_cfg80211.c -+++ b/mlinux/moal_uap_cfg80211.c -@@ -3,7 +3,7 @@ - * @brief This file contains the functions for uAP CFG80211. - * - * -- * Copyright 2011-2024 NXP -+ * Copyright 2011-2025 NXP - * - * This software file (the File) is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 -@@ -2780,8 +2780,8 @@ int woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, - } - } - if (vir_priv && vir_priv->bss_type == MLAN_BSS_TYPE_UAP) { --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - if (woal_cfg80211_del_beacon(wiphy, dev, 0)) - #else - if (woal_cfg80211_del_beacon(wiphy, dev)) -@@ -2789,15 +2789,15 @@ int woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, - PRINTM(MERROR, "%s: del_beacon failed\n", - __func__); - #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - vir_priv->wdev->links[0].ap.beacon_interval = 0; - #else - vir_priv->wdev->beacon_interval = 0; - #endif - #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - memset(&vir_priv->wdev->links[0].ap.chandef, 0, - sizeof(vir_priv->wdev->links[0].ap.chandef)); - #else -@@ -2806,8 +2806,8 @@ int woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, - #endif - #endif - #endif --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - vir_priv->wdev->u.ap.ssid_len = 0; - #else - vir_priv->wdev->ssid_len = 0; -@@ -3101,8 +3101,8 @@ done: - * - * @return 0 -- success, otherwise fail - */ --#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || IMX_ANDROID_13 || \ -- IMX_ANDROID_12_BACKPORT) -+#if ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - int woal_cfg80211_del_beacon(struct wiphy *wiphy, struct net_device *dev, - unsigned int link_id) - #else -@@ -3168,10 +3168,7 @@ int woal_cfg80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) - if (woal_11h_cancel_chan_report_ioctl(priv, MOAL_IOCTL_WAIT)) - PRINTM(MERROR, "%s: cancel chan report failed \n", - __func__); --#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) -- cfg80211_cac_event(priv->netdev, &priv->phandle->dfs_channel, -- NL80211_RADAR_CAC_ABORTED, GFP_KERNEL, 0); --#elif CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) -+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) - cfg80211_cac_event(priv->netdev, &priv->phandle->dfs_channel, - NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); - #else -@@ -3851,10 +3848,7 @@ void woal_cac_timer_func(void *context) - moal_private *priv = handle->priv[handle->cac_bss_index]; - - PRINTM(MEVENT, "cac_timer fired.\n"); --#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) -- cfg80211_cac_event(priv->netdev, &handle->dfs_channel, -- NL80211_RADAR_CAC_ABORTED, GFP_KERNEL, 0); --#elif CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) -+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) - cfg80211_cac_event(priv->netdev, &handle->dfs_channel, - NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); - #else -@@ -3957,11 +3951,11 @@ static void woal_switch_uap_channel(moal_private *priv, t_u8 wait_option) - CFG80211_VERSION_CODE < KERNEL_VERSION(6, 9, 0) - cfg80211_ch_switch_notify(priv->netdev, &priv->chan, 0, 0); - #elif ((CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) && \ -- IMX_ANDROID_13)) && \ -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 33))) && \ - CFG80211_VERSION_CODE < KERNEL_VERSION(6, 9, 0) - cfg80211_ch_switch_notify(priv->netdev, &priv->chan, 0, 0); - #elif ((CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 19, 2)) || \ -- IMX_ANDROID_13 || IMX_ANDROID_12_BACKPORT) -+ (defined(ANDROID_SDK_VERSION) && ANDROID_SDK_VERSION >= 31)) - cfg80211_ch_switch_notify(priv->netdev, &priv->chan, 0); - #else - cfg80211_ch_switch_notify(priv->netdev, &priv->chan); -@@ -4013,10 +4007,7 @@ void woal_process_cancel_chanrpt_event(moal_private *priv) - if (woal_11h_cancel_chan_report_ioctl(priv, MOAL_IOCTL_WAIT)) - PRINTM(MERROR, "%s: cancel chan report failed \n", - __func__); --#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) -- cfg80211_cac_event(priv->netdev, &priv->phandle->dfs_channel, -- NL80211_RADAR_CAC_ABORTED, GFP_KERNEL, 0); --#elif CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) -+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) - cfg80211_cac_event(priv->netdev, &priv->phandle->dfs_channel, - NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); - #else -@@ -4030,22 +4021,7 @@ void woal_process_cancel_chanrpt_event(moal_private *priv) - } - #endif - --#if CFG80211_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) --/** -- * @brief start radar detection -- * -- * @param wiphy A pointer to wiphy structure -- * @param dev A pointer to net_device structure -- * @param chandef A pointer to cfg80211_chan_def structure -- * @param cac_time_ms A cac dwell time -- * @param link_id valid link_id for MLO operation or 0 otherwise. -- * @return 0 -- success, otherwise fail -- */ --int woal_cfg80211_start_radar_detection(struct wiphy *wiphy, -- struct net_device *dev, -- struct cfg80211_chan_def *chandef, -- u32 cac_time_ms, int link_id) --#elif CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) -+#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) - /** - * @brief start radar detection - * diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-kernel/kernel-modules/kernel-module-nxp-wlan_git.bbappend b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-kernel/kernel-modules/kernel-module-nxp-wlan_git.bbappend index 77a7a55d0..4c5b73ed1 100644 --- a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-kernel/kernel-modules/kernel-module-nxp-wlan_git.bbappend +++ b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-kernel/kernel-modules/kernel-module-nxp-wlan_git.bbappend @@ -5,7 +5,6 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/${BPN}:" SRC_URI:append = " \ file://81-iw612-wifi.rules \ file://load_iw612.sh \ - file://0001-issue-fix-wlan_src_driver_patch_release_lf-6.6.52-2..patch \ " do_install:append () { From 47dbfee270ee454dc81c175794029ff6ca7ae867 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Fri, 4 Apr 2025 11:15:29 +0200 Subject: [PATCH 040/138] trustfence-sign-artifact: fix kernel load address for u-boot v2024 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit U-boot v2024 changed the default load address (SYS_LOAD_ADDR) for CC8M platforms to: * Ensure the kernel load address is 2MB-aligned (avoiding extra memory copy) * Prevent overlap with the fixed DEK blob address at 0x40400000     Update the boot artifacts signing script accordingly. https://onedigi.atlassian.net/browse/DEL-9537 https://onedigi.atlassian.net/browse/DEL-9541 https://onedigi.atlassian.net/browse/DUB-1099 Signed-off-by: Javier Viguera --- .../trustfence-sign-tools/trustfence-sign-artifact-nxp.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meta-digi-arm/recipes-digi/trustfence/trustfence-sign-tools/trustfence-sign-artifact-nxp.sh b/meta-digi-arm/recipes-digi/trustfence/trustfence-sign-tools/trustfence-sign-artifact-nxp.sh index f14db3bf8..091af1a1f 100755 --- a/meta-digi-arm/recipes-digi/trustfence/trustfence-sign-tools/trustfence-sign-artifact-nxp.sh +++ b/meta-digi-arm/recipes-digi/trustfence/trustfence-sign-tools/trustfence-sign-artifact-nxp.sh @@ -98,8 +98,8 @@ done<<-_EOF_ ccimx6 0x12000000 0x19000000 0x18000000 - HAB 0x4000 "-h 4 -d sha256" ccimx6qp 0x12000000 0x19000000 0x18000000 - HAB 0x4000 "-h 4 -d sha256" ccimx6ul 0x80800000 0x83800000 0x83000000 - HAB 0x4000 "-h 4 -d sha256" - ccimx8mm 0x40480000 0x43800000 0x43000000 - HAB 0x2000 "-h 4 -d sha256" - ccimx8mn 0x40480000 0x43800000 0x43000000 - HAB 0x2000 "-h 4 -d sha256" + ccimx8mm 0x40600000 0x43800000 0x43000000 - HAB 0x2000 "-h 4 -d sha256" + ccimx8mn 0x40600000 0x43800000 0x43000000 - HAB 0x2000 "-h 4 -d sha256" ccimx8x 0x80280000 0x82100000 0x82000000 - AHAB - "-a -d sha512 -s sha512" ccimx91 - - - 0x84000000 AHAB - "-a -d sha256 -s sha512" ccimx93 - - - 0x84000000 AHAB - "-a -d sha256 -s sha512" From 83eb180adbd6756815c01850e8db08c7d09891aa Mon Sep 17 00:00:00 2001 From: Isaac Hermida Date: Tue, 1 Apr 2025 16:28:34 +0200 Subject: [PATCH 041/138] linux-dey: btnxpuart: include it for specific machine The btnxpuart is the driver used to manage the bluetooth for our i.mx9 modules, so only specify it for the affected platforms. Signed-off-by: Isaac Hermida --- meta-digi-arm/recipes-kernel/linux/linux-dey_6.6.bb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 36cc85a0c..213a1ce88 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 @@ -24,8 +24,8 @@ STM_RT_CONFIG_FRAGS = " \ 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 += "btnxpuart" -module_conf_btnxpuart = "blacklist btnxpuart" +KERNEL_MODULE_PROBECONF:ccimx9 += "btnxpuart" +module_conf_btnxpuart:ccimx9 = "blacklist btnxpuart" # --------------------------------------------------------------------- # stub for devicetree which are located on digi directory From 1a08f346cc40621cb123fe9a6e6a928d15780431 Mon Sep 17 00:00:00 2001 From: Isaac Hermida Date: Tue, 1 Apr 2025 11:21:14 +0200 Subject: [PATCH 042/138] 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" From 8ec26356543d20c73ed8e92b78f26427c8d56c04 Mon Sep 17 00:00:00 2001 From: Gabriel Valcazar Date: Tue, 8 Apr 2025 12:05:40 +0200 Subject: [PATCH 043/138] wpewebkit: add workaround to improve memory usage in ConnectCore Demo app Even though we launch said demo with WebKit's page cache disabled, WebKit still caches most of the demo's contents for two reasons: * The demo is browsed using a non-HTTP(S) protocol (file://) * The demo is contained within the same domain (same-site browsing) This can lead to unexpected behavior on platforms with small RAM size. Add a workaround to force WebKit to create a new web process everytime we open a page on the demo, deleting the previous process and freeing its memory. An alternative solution would be to alter our demo to make sure a page's resources are correctly freed when exiting said page, but this provides a general solution that ensures memory cleanup in all cases, albeit with slightly longer loading times. https://onedigi.atlassian.net/browse/DEL-9498 Signed-off-by: Gabriel Valcazar --- ...cessPool-always-swap-process-when-us.patch | 62 +++++++++++++++++++ .../wpewebkit/wpewebkit_2.44.4.bbappend | 1 + 2 files changed, 63 insertions(+) create mode 100644 meta-digi-dey/dynamic-layers/webkit/recipes-browser/wpewebkit/wpewebkit/0002-UIProcess-WebProcessPool-always-swap-process-when-us.patch diff --git a/meta-digi-dey/dynamic-layers/webkit/recipes-browser/wpewebkit/wpewebkit/0002-UIProcess-WebProcessPool-always-swap-process-when-us.patch b/meta-digi-dey/dynamic-layers/webkit/recipes-browser/wpewebkit/wpewebkit/0002-UIProcess-WebProcessPool-always-swap-process-when-us.patch new file mode 100644 index 000000000..840553961 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/webkit/recipes-browser/wpewebkit/wpewebkit/0002-UIProcess-WebProcessPool-always-swap-process-when-us.patch @@ -0,0 +1,62 @@ +From: Gabriel Valcazar +Date: Mon, 7 Apr 2025 12:02:30 +0200 +Subject: [PATCH] UIProcess: WebProcessPool: always swap process when using + non-HTTP(S) protocols + +When browsing through a local web application via the file:// protocol, the +same WPEWebProcess is re-used for all pages. Browsing through several memory +intensive pages in a row (especially multimedia) causes the web process to hog +lots of memory, which can cause issues on systems with small RAM sizes. For +example, on ConnectCore platforms with 1 GiB of RAM, opening a WebGL sample +followed by playing a video sometimes causes a kernel panic due to extremely +low CMA memory, which is needed for the video decoding process. + +To avoid this, make sure a new WPEWebProcess is generated each time a page is +opened on non-HTTP(S) protocols such as file://. This adds some loading time +overhead, but it assures that each page's memory is freed when exiting it, +drastically reducing the chances of unexpected behavior. To accomplish this: + + * Set m_processSwapsOnNavigationWithinSameNonHTTPFamilyProtocol to "true" + to ensure the web process isn't re-used when navigating with a + non-HTTP(S) protocol. The WPE version of WebKit doesn't expose the API + that can change this value at runtime, so simply change the default value + * Skip the same-site navigation check when non-HTTP(S) process swap is + enabled and the protocol is non-HTTP(S). Otherwise, the same web process + will get re-used even if non-HTTP(S) process swap is enabled. + +Signed-off-by: Gabriel Valcazar +--- + Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.h | 2 +- + Source/WebKit/UIProcess/WebProcessPool.cpp | 6 ++++-- + 2 files changed, 5 insertions(+), 3 deletions(-) + +diff --git a/Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.h b/Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.h +index dbbfea6b..6574f435 100644 +--- a/Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.h ++++ b/Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.h +@@ -174,7 +174,7 @@ private: + std::optional m_processSwapsOnNavigationFromClient; + bool m_processSwapsOnNavigationFromExperimentalFeatures { false }; + bool m_alwaysKeepAndReuseSwappedProcesses { false }; +- bool m_processSwapsOnNavigationWithinSameNonHTTPFamilyProtocol { false }; ++ bool m_processSwapsOnNavigationWithinSameNonHTTPFamilyProtocol { true }; + std::optional m_isAutomaticProcessWarmingEnabledByClient; + bool m_usesWebProcessCache { false }; + bool m_usesBackForwardCache { true }; +diff --git a/Source/WebKit/UIProcess/WebProcessPool.cpp b/Source/WebKit/UIProcess/WebProcessPool.cpp +index 1b147534..5b541aad 100644 +--- a/Source/WebKit/UIProcess/WebProcessPool.cpp ++++ b/Source/WebKit/UIProcess/WebProcessPool.cpp +@@ -2138,8 +2138,10 @@ std::tuple, SuspendedPageProxy*, ASCIILiteral> WebProcessPo + if (!m_configuration->processSwapsOnNavigationWithinSameNonHTTPFamilyProtocol() && !sourceURL.protocolIsInHTTPFamily() && sourceURL.protocol() == targetURL.protocol()) + return { WTFMove(sourceProcess), nullptr, "Navigation within the same non-HTTP(s) protocol"_s }; + +- if (!sourceURL.isValid() || !targetURL.isValid() || sourceURL.isEmpty() || targetRegistrableDomain.matches(sourceURL) || (sourceURL.protocolIsAbout() && (!sourceProcess->hasCommittedAnyMeaningfulProvisionalLoads() || sourceProcess->registrableDomain().matches(targetURL))) || targetRegistrableDomain.matches(sourceURL)) +- return { WTFMove(sourceProcess), nullptr, "Navigation is same-site"_s }; ++ if (!m_configuration->processSwapsOnNavigationWithinSameNonHTTPFamilyProtocol() || (sourceURL.protocolIsInHTTPFamily() && sourceURL.protocol() == targetURL.protocol())) { ++ if (!sourceURL.isValid() || !targetURL.isValid() || sourceURL.isEmpty() || targetRegistrableDomain.matches(sourceURL) || (sourceURL.protocolIsAbout() && (!sourceProcess->hasCommittedAnyMeaningfulProvisionalLoads() || sourceProcess->registrableDomain().matches(targetURL))) || targetRegistrableDomain.matches(sourceURL)) ++ return { WTFMove(sourceProcess), nullptr, "Navigation is same-site"_s }; ++ } + + auto reason = "Navigation is cross-site"_s; + diff --git a/meta-digi-dey/dynamic-layers/webkit/recipes-browser/wpewebkit/wpewebkit_2.44.4.bbappend b/meta-digi-dey/dynamic-layers/webkit/recipes-browser/wpewebkit/wpewebkit_2.44.4.bbappend index 4b353af60..333c248d5 100644 --- a/meta-digi-dey/dynamic-layers/webkit/recipes-browser/wpewebkit/wpewebkit_2.44.4.bbappend +++ b/meta-digi-dey/dynamic-layers/webkit/recipes-browser/wpewebkit/wpewebkit_2.44.4.bbappend @@ -4,6 +4,7 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/${BPN}:" # Backport patch to fix build with "lbse" disabled SRC_URI:append = " \ file://0001-Build-fix-when-LAYER_BASED_SVG_ENGINE-is-off.patch \ + file://0002-UIProcess-WebProcessPool-always-swap-process-when-us.patch \ " SRC_URI:append:ccimx8x = " \ From ccb98366eacfdfeb9b1be5992eecbebb4e9d5ecf Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Tue, 1 Apr 2025 13:45:06 +0200 Subject: [PATCH 044/138] dey-image-flutter: add new recipe based on graphical Flutter framework Flutter is an open source framework for building multi-platform applications without a graphical backend running. This commit adds support to create a new DEY image type based on Flutter ready to use for the users. https://onedigi.atlassian.net/browse/DEL-9380 Signed-off-by: Arturo Buzarra --- .../nativesdk-packagegroup-sdk-host.bbappend | 3 +++ meta-digi-dey/conf/distro/dey.conf | 1 + .../recipes-core/images/dey-image-flutter.bb | 26 +++++++++++++++++++ .../packagegroups/packagegroup-dey-flutter.bb | 16 ++++++++++++ 4 files changed, 46 insertions(+) create mode 100644 meta-digi-dey/recipes-core/images/dey-image-flutter.bb create mode 100644 meta-digi-dey/recipes-graphics/packagegroups/packagegroup-dey-flutter.bb diff --git a/meta-digi-arm/recipes-core/packagegroup/nativesdk-packagegroup-sdk-host.bbappend b/meta-digi-arm/recipes-core/packagegroup/nativesdk-packagegroup-sdk-host.bbappend index 1df3246f1..31326324a 100644 --- a/meta-digi-arm/recipes-core/packagegroup/nativesdk-packagegroup-sdk-host.bbappend +++ b/meta-digi-arm/recipes-core/packagegroup/nativesdk-packagegroup-sdk-host.bbappend @@ -18,3 +18,6 @@ RDEPENDS:${PN} += " \ ${@oe.utils.conditional('DEY_SOC_VENDOR', 'NXP', '${IMX_OPTEE_SDK_RDEPENDS} ${IMX_TRUSTFENCE_SDK_TOOLS}', '', d)} \ ${@oe.utils.conditional('DEY_SOC_VENDOR', 'STM', '${STM_TRUSTFENCE_SDK_TOOLS}', '', d)} \ " + +RDEPENDS:${PN}:append:ccmp25 = " nativesdk-flutter-sdk" +RDEPENDS:${PN}:append:imxgpu = " nativesdk-flutter-sdk" diff --git a/meta-digi-dey/conf/distro/dey.conf b/meta-digi-dey/conf/distro/dey.conf index b8aa37b62..825cb5c4a 100644 --- a/meta-digi-dey/conf/distro/dey.conf +++ b/meta-digi-dey/conf/distro/dey.conf @@ -46,6 +46,7 @@ FEATURE_PACKAGES_dey-audio = "packagegroup-dey-audio" FEATURE_PACKAGES_dey-bluetooth = "packagegroup-dey-bluetooth" FEATURE_PACKAGES_dey-debug = "packagegroup-dey-debug" FEATURE_PACKAGES_dey-examples = "packagegroup-dey-examples" +FEATURE_PACKAGES_dey-flutter = "packagegroup-dey-flutter" FEATURE_PACKAGES_dey-gstreamer = "packagegroup-dey-gstreamer" FEATURE_PACKAGES_dey-lvgl = "packagegroup-dey-lvgl" FEATURE_PACKAGES_dey-network = "packagegroup-dey-network" diff --git a/meta-digi-dey/recipes-core/images/dey-image-flutter.bb b/meta-digi-dey/recipes-core/images/dey-image-flutter.bb new file mode 100644 index 000000000..77667d80a --- /dev/null +++ b/meta-digi-dey/recipes-core/images/dey-image-flutter.bb @@ -0,0 +1,26 @@ +# Copyright (C) 2025, Digi International Inc. + +DESCRIPTION = "DEY image with Flutter graphical libraries" +LICENSE = "MIT" + +IMAGE_INSTALL = " \ + packagegroup-dey-core \ + ${CORE_IMAGE_EXTRA_INSTALL} \ +" + +IMAGE_FEATURES += " \ + dey-network \ + dey-flutter \ + eclipse-debug \ + ssh-server-dropbear \ + ${@bb.utils.contains('MACHINE_FEATURES', 'alsa', 'dey-audio', '', d)} \ + ${@bb.utils.contains('MACHINE_FEATURES', 'bluetooth', 'dey-bluetooth', '', d)} \ + ${@bb.utils.contains('MACHINE_FEATURES', 'wifi', 'dey-wireless', '', d)} \ +" + +IMAGE_LINGUAS = "" + +inherit core-image +inherit dey-image + +IMAGE_ROOTFS_SIZE = "8192" diff --git a/meta-digi-dey/recipes-graphics/packagegroups/packagegroup-dey-flutter.bb b/meta-digi-dey/recipes-graphics/packagegroups/packagegroup-dey-flutter.bb new file mode 100644 index 000000000..1d8a1f582 --- /dev/null +++ b/meta-digi-dey/recipes-graphics/packagegroups/packagegroup-dey-flutter.bb @@ -0,0 +1,16 @@ +# +# Copyright (C) 2025, Digi International Inc. +# +SUMMARY = "Flutter packagegroup for DEY image" + +PACKAGE_ARCH = "${MACHINE_ARCH}" +inherit packagegroup + +RDEPENDS:${PN} += " \ + flutter-pi \ + flutter-samples-veggieseasons \ +" + +RDEPENDS:${PN}:append:imxgpu = " \ + imx-gpu-viv \ +" From 698b32ed3116c9dffe9163618b9e2a844f9f2095 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Tue, 1 Apr 2025 13:55:10 +0200 Subject: [PATCH 045/138] templates: add support to Flutter framework https://onedigi.atlassian.net/browse/DEL-9380 Signed-off-by: Arturo Buzarra --- .../conf/templates/ccimx8mm-dvk/bblayers.conf.sample | 3 +++ meta-digi-arm/conf/templates/ccimx8mm-dvk/conf-notes.txt | 5 +++++ .../conf/templates/ccimx8mn-dvk/bblayers.conf.sample | 3 +++ meta-digi-arm/conf/templates/ccimx8mn-dvk/conf-notes.txt | 5 +++++ .../conf/templates/ccimx8x-sbc-express/bblayers.conf.sample | 3 +++ .../conf/templates/ccimx8x-sbc-express/conf-notes.txt | 5 +++++ .../conf/templates/ccimx8x-sbc-pro/bblayers.conf.sample | 3 +++ meta-digi-arm/conf/templates/ccimx8x-sbc-pro/conf-notes.txt | 5 +++++ meta-digi-arm/conf/templates/ccmp25-dvk/bblayers.conf.sample | 3 +++ meta-digi-arm/conf/templates/ccmp25-dvk/conf-notes.txt | 5 +++++ 10 files changed, 40 insertions(+) diff --git a/meta-digi-arm/conf/templates/ccimx8mm-dvk/bblayers.conf.sample b/meta-digi-arm/conf/templates/ccimx8mm-dvk/bblayers.conf.sample index e35a0876f..490fec00d 100644 --- a/meta-digi-arm/conf/templates/ccimx8mm-dvk/bblayers.conf.sample +++ b/meta-digi-arm/conf/templates/ccimx8mm-dvk/bblayers.conf.sample @@ -22,6 +22,9 @@ BBLAYERS ?= " \ ##DIGIBASE##/meta-imx/meta-imx-ml \ ##DIGIBASE##/meta-selinux \ ##DIGIBASE##/meta-timesys \ + ##DIGIBASE##/meta-flutter \ + ##DIGIBASE##/meta-flutter/meta-flutter-apps \ + ##DIGIBASE##/meta-clang \ ##DIGIBASE##/meta-digi/meta-digi-arm \ ##DIGIBASE##/meta-digi/meta-digi-dey \ " diff --git a/meta-digi-arm/conf/templates/ccimx8mm-dvk/conf-notes.txt b/meta-digi-arm/conf/templates/ccimx8mm-dvk/conf-notes.txt index 7c10edbcf..365bdb1bb 100644 --- a/meta-digi-arm/conf/templates/ccimx8mm-dvk/conf-notes.txt +++ b/meta-digi-arm/conf/templates/ccimx8mm-dvk/conf-notes.txt @@ -14,3 +14,8 @@ Digi Embedded Yocto provides the following image recipes: By default the image is XWayland-based so it provides a full Weston desktop environment. + + * dey-image-flutter: graphical Flutter image + + Expansion of custom Digi core-image-base including full Flutter + framework support. diff --git a/meta-digi-arm/conf/templates/ccimx8mn-dvk/bblayers.conf.sample b/meta-digi-arm/conf/templates/ccimx8mn-dvk/bblayers.conf.sample index e35a0876f..490fec00d 100644 --- a/meta-digi-arm/conf/templates/ccimx8mn-dvk/bblayers.conf.sample +++ b/meta-digi-arm/conf/templates/ccimx8mn-dvk/bblayers.conf.sample @@ -22,6 +22,9 @@ BBLAYERS ?= " \ ##DIGIBASE##/meta-imx/meta-imx-ml \ ##DIGIBASE##/meta-selinux \ ##DIGIBASE##/meta-timesys \ + ##DIGIBASE##/meta-flutter \ + ##DIGIBASE##/meta-flutter/meta-flutter-apps \ + ##DIGIBASE##/meta-clang \ ##DIGIBASE##/meta-digi/meta-digi-arm \ ##DIGIBASE##/meta-digi/meta-digi-dey \ " diff --git a/meta-digi-arm/conf/templates/ccimx8mn-dvk/conf-notes.txt b/meta-digi-arm/conf/templates/ccimx8mn-dvk/conf-notes.txt index 7c10edbcf..365bdb1bb 100644 --- a/meta-digi-arm/conf/templates/ccimx8mn-dvk/conf-notes.txt +++ b/meta-digi-arm/conf/templates/ccimx8mn-dvk/conf-notes.txt @@ -14,3 +14,8 @@ Digi Embedded Yocto provides the following image recipes: By default the image is XWayland-based so it provides a full Weston desktop environment. + + * dey-image-flutter: graphical Flutter image + + Expansion of custom Digi core-image-base including full Flutter + framework support. diff --git a/meta-digi-arm/conf/templates/ccimx8x-sbc-express/bblayers.conf.sample b/meta-digi-arm/conf/templates/ccimx8x-sbc-express/bblayers.conf.sample index 974e731e4..c43a5d05a 100644 --- a/meta-digi-arm/conf/templates/ccimx8x-sbc-express/bblayers.conf.sample +++ b/meta-digi-arm/conf/templates/ccimx8x-sbc-express/bblayers.conf.sample @@ -20,6 +20,9 @@ BBLAYERS ?= " \ ##DIGIBASE##/meta-webkit \ ##DIGIBASE##/meta-selinux \ ##DIGIBASE##/meta-timesys \ + ##DIGIBASE##/meta-flutter \ + ##DIGIBASE##/meta-flutter/meta-flutter-apps \ + ##DIGIBASE##/meta-clang \ ##DIGIBASE##/meta-digi/meta-digi-arm \ ##DIGIBASE##/meta-digi/meta-digi-dey \ " diff --git a/meta-digi-arm/conf/templates/ccimx8x-sbc-express/conf-notes.txt b/meta-digi-arm/conf/templates/ccimx8x-sbc-express/conf-notes.txt index 7c10edbcf..365bdb1bb 100644 --- a/meta-digi-arm/conf/templates/ccimx8x-sbc-express/conf-notes.txt +++ b/meta-digi-arm/conf/templates/ccimx8x-sbc-express/conf-notes.txt @@ -14,3 +14,8 @@ Digi Embedded Yocto provides the following image recipes: By default the image is XWayland-based so it provides a full Weston desktop environment. + + * dey-image-flutter: graphical Flutter image + + Expansion of custom Digi core-image-base including full Flutter + framework support. diff --git a/meta-digi-arm/conf/templates/ccimx8x-sbc-pro/bblayers.conf.sample b/meta-digi-arm/conf/templates/ccimx8x-sbc-pro/bblayers.conf.sample index e35a0876f..490fec00d 100644 --- a/meta-digi-arm/conf/templates/ccimx8x-sbc-pro/bblayers.conf.sample +++ b/meta-digi-arm/conf/templates/ccimx8x-sbc-pro/bblayers.conf.sample @@ -22,6 +22,9 @@ BBLAYERS ?= " \ ##DIGIBASE##/meta-imx/meta-imx-ml \ ##DIGIBASE##/meta-selinux \ ##DIGIBASE##/meta-timesys \ + ##DIGIBASE##/meta-flutter \ + ##DIGIBASE##/meta-flutter/meta-flutter-apps \ + ##DIGIBASE##/meta-clang \ ##DIGIBASE##/meta-digi/meta-digi-arm \ ##DIGIBASE##/meta-digi/meta-digi-dey \ " diff --git a/meta-digi-arm/conf/templates/ccimx8x-sbc-pro/conf-notes.txt b/meta-digi-arm/conf/templates/ccimx8x-sbc-pro/conf-notes.txt index 7c10edbcf..365bdb1bb 100644 --- a/meta-digi-arm/conf/templates/ccimx8x-sbc-pro/conf-notes.txt +++ b/meta-digi-arm/conf/templates/ccimx8x-sbc-pro/conf-notes.txt @@ -14,3 +14,8 @@ Digi Embedded Yocto provides the following image recipes: By default the image is XWayland-based so it provides a full Weston desktop environment. + + * dey-image-flutter: graphical Flutter image + + Expansion of custom Digi core-image-base including full Flutter + framework support. diff --git a/meta-digi-arm/conf/templates/ccmp25-dvk/bblayers.conf.sample b/meta-digi-arm/conf/templates/ccmp25-dvk/bblayers.conf.sample index bdbe740ee..74aeec820 100644 --- a/meta-digi-arm/conf/templates/ccmp25-dvk/bblayers.conf.sample +++ b/meta-digi-arm/conf/templates/ccmp25-dvk/bblayers.conf.sample @@ -20,6 +20,9 @@ BBLAYERS ?= " \ ##DIGIBASE##/meta-timesys \ ##DIGIBASE##/meta-st-stm32mp \ ##DIGIBASE##/meta-st-x-linux-ai \ + ##DIGIBASE##/meta-flutter \ + ##DIGIBASE##/meta-flutter/meta-flutter-apps \ + ##DIGIBASE##/meta-clang \ ##DIGIBASE##/meta-digi/meta-digi-arm \ ##DIGIBASE##/meta-digi/meta-digi-dey \ " diff --git a/meta-digi-arm/conf/templates/ccmp25-dvk/conf-notes.txt b/meta-digi-arm/conf/templates/ccmp25-dvk/conf-notes.txt index a0a98b6e5..ed9e5d62f 100644 --- a/meta-digi-arm/conf/templates/ccmp25-dvk/conf-notes.txt +++ b/meta-digi-arm/conf/templates/ccmp25-dvk/conf-notes.txt @@ -15,6 +15,11 @@ Digi Embedded Yocto provides the following image recipes: By default the image is Wayland-based so it provides a full Weston desktop environment. + * dey-image-flutter: graphical Flutter image + + Expansion of custom Digi core-image-base including full Flutter + framework support. + * core-image-base: a console-only image Expansion of native core-image-base by including all the support for the From 759598fdc0f2b8cfa95a4f865ac66d94261a77a5 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Mon, 14 Apr 2025 14:38:22 +0200 Subject: [PATCH 046/138] ccimx8mm: add OP-TEE support for all memory variants * Set the OP-TEE base address to 0x7e000000 (32MiB below the first gigabyte of DDR). * Update the ATF and OP-TEE memory maps to support up to 4GiB DDR. This ensures OP-TEE runs reliably across all ccimx8mm memory configurations. https://onedigi.atlassian.net/browse/DEL-9502 Signed-off-by: Javier Viguera --- ...BASE-and-map-high-DRAM-for-ccimx8mm-.patch | 67 +++++++++++++++++++ .../recipes-bsp/imx-atf/imx-atf_%.bbappend | 1 + ...mm-adjust-TEE_LOAD_ADDR-for-ccimx8mm.patch | 26 +++++++ .../imx-mkimage/imx-boot_1.0.bbappend | 1 + ...-add-support-for-ConnectCore-8M-Mini.patch | 55 +++++++++++++++ .../optee-imx/optee-os_4.4.0.imx.bbappend | 5 ++ 6 files changed, 155 insertions(+) create mode 100644 meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-atf/imx-atf/0005-imx8mm-set-BL32_BASE-and-map-high-DRAM-for-ccimx8mm-.patch create mode 100644 meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/files/0004-imx8mm-adjust-TEE_LOAD_ADDR-for-ccimx8mm.patch create mode 100644 meta-digi-arm/dynamic-layers/freescale-layer/recipes-security/optee-imx/optee-os/0001-plat-imx-add-support-for-ConnectCore-8M-Mini.patch diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-atf/imx-atf/0005-imx8mm-set-BL32_BASE-and-map-high-DRAM-for-ccimx8mm-.patch b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-atf/imx-atf/0005-imx8mm-set-BL32_BASE-and-map-high-DRAM-for-ccimx8mm-.patch new file mode 100644 index 000000000..7d359d352 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-atf/imx-atf/0005-imx8mm-set-BL32_BASE-and-map-high-DRAM-for-ccimx8mm-.patch @@ -0,0 +1,67 @@ +From: Javier Viguera +Date: Fri, 11 Apr 2025 15:01:33 +0200 +Subject: [PATCH] imx8mm: set BL32_BASE and map high DRAM for ccimx8mm support + +Set BL32_BASE to 0x7e000000 (32MiB below the first gigabyte of DDR) to +align with the OP-TEE layout used across ccimx8mm variants. Add support +for high-memory DDR by defining and mapping an extra 1GB mem at 0x100000000 +to support the 4GB variant. + +Upstream-Status: Inappropriate [DEY specific] + +Signed-off-by: Javier Viguera +--- + plat/imx/imx8m/imx8mm/imx8mm_bl31_setup.c | 1 + + plat/imx/imx8m/imx8mm/include/platform_def.h | 6 ++++-- + plat/imx/imx8m/imx8mm/platform.mk | 2 +- + 3 files changed, 6 insertions(+), 3 deletions(-) + +diff --git a/plat/imx/imx8m/imx8mm/imx8mm_bl31_setup.c b/plat/imx/imx8m/imx8mm/imx8mm_bl31_setup.c +index 388deae2b0e8..76f8a34d12fb 100644 +--- a/plat/imx/imx8m/imx8mm/imx8mm_bl31_setup.c ++++ b/plat/imx/imx8m/imx8mm/imx8mm_bl31_setup.c +@@ -51,6 +51,7 @@ static const mmap_region_t imx_mmap[] = { + MAP_REGION_FLAT(IMX_ROM_BASE, IMX_ROM_SIZE, MT_MEMORY | MT_RO), /* ROM code */ + #ifndef PLAT_XLAT_TABLES_DYNAMIC + MAP_REGION_FLAT(IMX_DRAM_BASE, IMX_DRAM_SIZE, MT_MEMORY | MT_RW | MT_NS), /* DRAM */ ++ MAP_REGION_FLAT(IMX_DRAM2_BASE, IMX_DRAM2_SIZE, MT_MEMORY | MT_RW | MT_NS), /* DRAM */ + #endif + MAP_REGION_FLAT(IMX_TCM_BASE, IMX_TCM_SIZE, MT_MEMORY | MT_RW | MT_NS), /* TCM */ + {0}, +diff --git a/plat/imx/imx8m/imx8mm/include/platform_def.h b/plat/imx/imx8m/imx8mm/include/platform_def.h +index e721664087f8..44926451b2a2 100644 +--- a/plat/imx/imx8m/imx8mm/include/platform_def.h ++++ b/plat/imx/imx8m/imx8mm/include/platform_def.h +@@ -69,8 +69,8 @@ + #define PLAT_GICD_BASE U(0x38800000) + #define PLAT_GICR_BASE U(0x38880000) + +-#define PLAT_VIRT_ADDR_SPACE_SIZE (1ull << 32) +-#define PLAT_PHY_ADDR_SPACE_SIZE (1ull << 32) ++#define PLAT_VIRT_ADDR_SPACE_SIZE (1ull << 34) ++#define PLAT_PHY_ADDR_SPACE_SIZE (1ull << 34) + + #ifdef SPD_trusty + #define MAX_XLAT_TABLES 10 +@@ -125,6 +125,8 @@ + #define IMX_CAAM_RAM_SIZE U(0x10000) + #define IMX_DRAM_BASE U(0x40000000) + #define IMX_DRAM_SIZE U(0xc0000000) ++#define IMX_DRAM2_BASE U(0x100000000) ++#define IMX_DRAM2_SIZE U(0x40000000) + #define IMX_TCM_BASE U(0x7E0000) + #define IMX_TCM_SIZE U(0x40000) + +diff --git a/plat/imx/imx8m/imx8mm/platform.mk b/plat/imx/imx8m/imx8mm/platform.mk +index 1de30f40f397..4fe3cca34065 100644 +--- a/plat/imx/imx8m/imx8mm/platform.mk ++++ b/plat/imx/imx8m/imx8mm/platform.mk +@@ -166,7 +166,7 @@ ERRATA_A53_835769 := 1 + ERRATA_A53_843419 := 1 + ERRATA_A53_855873 := 1 + +-BL32_BASE ?= 0xbe000000 ++BL32_BASE ?= 0x7e000000 + $(eval $(call add_define,BL32_BASE)) + + BL32_SIZE ?= 0x2000000 diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-atf/imx-atf_%.bbappend b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-atf/imx-atf_%.bbappend index 7b3d1e503..336f4cf98 100644 --- a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-atf/imx-atf_%.bbappend +++ b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-atf/imx-atf_%.bbappend @@ -7,6 +7,7 @@ SRC_URI:append:ccimx8m = " \ file://0002-imx8mm-Disable-M4-debug-console.patch \ file://0003-imx8mn-Define-UART1-as-console-for-boot-stage.patch \ file://0004-imx8mn-Disable-M7-debug-console.patch \ + file://0005-imx8mm-set-BL32_BASE-and-map-high-DRAM-for-ccimx8mm-.patch \ " SRC_URI:append:ccimx91 = " \ file://0001-ccimx91-use-UART6-for-the-default-console.patch \ diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/files/0004-imx8mm-adjust-TEE_LOAD_ADDR-for-ccimx8mm.patch b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/files/0004-imx8mm-adjust-TEE_LOAD_ADDR-for-ccimx8mm.patch new file mode 100644 index 000000000..da4184245 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/files/0004-imx8mm-adjust-TEE_LOAD_ADDR-for-ccimx8mm.patch @@ -0,0 +1,26 @@ +From: Javier Viguera +Date: Mon, 7 Apr 2025 16:57:39 +0200 +Subject: [PATCH] imx8mm: adjust TEE_LOAD_ADDR for ccimx8mm + +This change aligns with equivalent changes in the Optee and ATF. + +Upstream-Status: Inappropriate [DEY specific] + +Signed-off-by: Javier Viguera +--- + iMX8M/soc.mak | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/iMX8M/soc.mak b/iMX8M/soc.mak +index ffd70af8ac7d..7ab1ea099894 100644 +--- a/iMX8M/soc.mak ++++ b/iMX8M/soc.mak +@@ -35,7 +35,7 @@ PLAT = imx8mm + HDMI = no + SPL_LOAD_ADDR = 0x7E1000 + SPL_FSPI_LOAD_ADDR = 0x7E2000 +-TEE_LOAD_ADDR ?= 0xbe000000 ++TEE_LOAD_ADDR ?= 0x7e000000 + ATF_LOAD_ADDR = 0x00920000 + VAL_BOARD = val + #define the F(Q)SPI header file diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/imx-boot_1.0.bbappend b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/imx-boot_1.0.bbappend index 67b8e2981..5cab81e18 100644 --- a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/imx-boot_1.0.bbappend +++ b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-mkimage/imx-boot_1.0.bbappend @@ -5,6 +5,7 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/files:" SRC_URI:append:ccimx8m = " \ file://0002-imx8m-soc.mak-capture-commands-output-into-a-log-fil.patch \ file://0003-imx8m-print_fit_hab-follow-symlinks.patch \ + file://0004-imx8mm-adjust-TEE_LOAD_ADDR-for-ccimx8mm.patch \ " SRC_URI:append:ccimx8x = " \ file://0001-iMX8QX-soc.mak-capture-commands-output-into-a-log-fi.patch \ diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-security/optee-imx/optee-os/0001-plat-imx-add-support-for-ConnectCore-8M-Mini.patch b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-security/optee-imx/optee-os/0001-plat-imx-add-support-for-ConnectCore-8M-Mini.patch new file mode 100644 index 000000000..f96f02ff2 --- /dev/null +++ b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-security/optee-imx/optee-os/0001-plat-imx-add-support-for-ConnectCore-8M-Mini.patch @@ -0,0 +1,55 @@ +From: Javier Viguera +Date: Fri, 11 Apr 2025 15:13:18 +0200 +Subject: [PATCH] plat-imx: add support for ConnectCore 8M Mini + +Add support for ccimx8mmdvk platform flavor: +* Define a DDR size that supports up to 4GB. +* Force TZDRAM base to 0x7e000000 for consistent OP-TEE base address + across all ccimx8mm variants, regardless of actual DDR size. +* Set UART base to UART1_BASE. + +Upstream-Status: Inappropriate [DEY specific] + +Signed-off-by: Javier Viguera +--- + core/arch/arm/plat-imx/conf.mk | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/core/arch/arm/plat-imx/conf.mk b/core/arch/arm/plat-imx/conf.mk +index d1fc2882e598..62cc51b61063 100644 +--- a/core/arch/arm/plat-imx/conf.mk ++++ b/core/arch/arm/plat-imx/conf.mk +@@ -63,6 +63,7 @@ mx8mq-flavorlist = \ + mx8mqevk + + mx8mm-flavorlist = \ ++ ccimx8mmdvk \ + mx8mmevk \ + mx8mm_cl_iot_gate + +@@ -412,6 +413,15 @@ CFG_DDR_SIZE ?= 0xc0000000 + CFG_UART_BASE ?= UART1_BASE + endif + ++ifneq (,$(filter $(PLATFORM_FLAVOR),ccimx8mmdvk)) ++# Configure DDR size for the variant with maximum memory ++CFG_DDR_SIZE ?= 0x100000000 ++CFG_UART_BASE ?= UART1_BASE ++$(call force,CFG_CCIMX8MM,y) ++$(call force,CFG_CORE_LARGE_PHYS_ADDR,y) ++$(call force,CFG_CORE_ARM64_PA_BITS,36) ++endif ++ + ifneq (,$(filter $(PLATFORM_FLAVOR),mx8mmevk)) + CFG_DDR_SIZE ?= 0x80000000 + CFG_UART_BASE ?= UART2_BASE +@@ -587,6 +597,9 @@ else ifneq (,$(filter y, $(CFG_MX95))) + CFG_TZDRAM_START ?= ($(CFG_DRAM_BASE) + 0x0C000000) + # On i.MX95 we will have 32MB OP-TEE memory and 2MB Shared Memory after that. + CFG_TZDRAM_SIZE ?= 0x02000000 ++else ifneq (,$(filter y, $(CFG_CCIMX8MM))) ++# Set the Optee base address at the end of the first GB (where 0x02000000 = CFG_TZDRAM_SIZE + CFG_SHMEM_SIZE). ++CFG_TZDRAM_START ?= ($(CFG_DRAM_BASE) - 0x02000000 + 0x40000000) + else ifneq (,$(filter y, $(CFG_MX8MM) $(CFG_MX8MQ) $(CFG_MX8QM) $(CFG_MX8QX))) + CFG_TZDRAM_START ?= ($(CFG_DRAM_BASE) - 0x02000000 + $(CFG_DDR_SIZE)) + else diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-security/optee-imx/optee-os_4.4.0.imx.bbappend b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-security/optee-imx/optee-os_4.4.0.imx.bbappend index 6f69344c7..be5ab0543 100644 --- a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-security/optee-imx/optee-os_4.4.0.imx.bbappend +++ b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-security/optee-imx/optee-os_4.4.0.imx.bbappend @@ -3,6 +3,10 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/${BPN}:" SRC_URI += "file://environment.d-optee-sdk.sh" +SRC_URI:append:ccimx8mm = " \ + file://0001-plat-imx-add-support-for-ConnectCore-8M-Mini.patch \ +" + SRC_URI:append:ccimx91 = " \ file://0001-core-imx-support-ccimx91-dvk.patch \ " @@ -12,6 +16,7 @@ SRC_URI:append:ccimx93 = " \ file://0002-core-ccimx93-enable-AES_HUK-trusted-application.patch \ " +PLATFORM_FLAVOR:ccimx8mm = "ccimx8mmdvk" PLATFORM_FLAVOR:ccimx91 = "ccimx91dvk" PLATFORM_FLAVOR:ccimx93 = "ccimx93dvk" From 758cae2e193c9b51ebaf1edd02b9b2f9e9c2e22c Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Tue, 15 Apr 2025 10:33:07 +0200 Subject: [PATCH 047/138] build.sh: add dey-image-flutter image https://onedigi.atlassian.net/browse/DEL-9380 Signed-off-by: Arturo Buzarra --- sdk/build.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/build.sh b/sdk/build.sh index 715215a35..badf6802a 100755 --- a/sdk/build.sh +++ b/sdk/build.sh @@ -172,9 +172,9 @@ while read -r _pl _tgt; do # the fly with underscores. eval "${_pl//-/_}_tgt=\"${_tgt//,/ }\"" done<<-_EOF_ - ccimx8mm-dvk dey-image-qt,dey-image-webkit,dey-image-lvgl - ccimx8mn-dvk dey-image-qt,dey-image-webkit,dey-image-lvgl - ccimx8x-sbc-pro dey-image-qt,dey-image-webkit,dey-image-lvgl + ccimx8mm-dvk dey-image-qt,dey-image-webkit,dey-image-lvgl,dey-image-flutter + ccimx8mn-dvk dey-image-qt,dey-image-webkit,dey-image-lvgl,dey-image-flutter + ccimx8x-sbc-pro dey-image-qt,dey-image-webkit,dey-image-lvgl,dey-image-flutter ccimx8x-sbc-express dey-image-qt ccimx6qpsbc dey-image-qt,dey-image-webkit,dey-image-lvgl ccimx6sbc dey-image-qt,dey-image-webkit,dey-image-lvgl @@ -184,7 +184,7 @@ done<<-_EOF_ ccimx6ulrftest dey-image-mft-module-rf ccmp15-dvk dey-image-qt,dey-image-webkit,dey-image-lvgl ccmp13-dvk core-image-base - ccmp25-dvk dey-image-qt,dey-image-webkit,dey-image-lvgl + ccmp25-dvk dey-image-qt,dey-image-webkit,dey-image-lvgl,dey-image-flutter ccimx91-dvk core-image-base ccimx93-dvk dey-image-qt,dey-image-lvgl _EOF_ From e61eaf172cf531155d51f823483dfd9fd0cc5119 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Tue, 22 Apr 2025 10:53:31 +0200 Subject: [PATCH 048/138] nativesdk-packagegroup-sdk-host: remove Flutter support by default This commit removes the default integration of the Flutter framework from the SDK due to its significant impact on toolchain size, build time, and reliability. Including Flutter increases the build complexity exponentially, often resulting in timeouts or failures caused by the large number of recipes involved. Customers who require Flutter can still enable it manually if needed. https://onedigi.atlassian.net/browse/DEL-9380 Signed-off-by: Arturo Buzarra --- .../packagegroup/nativesdk-packagegroup-sdk-host.bbappend | 3 --- 1 file changed, 3 deletions(-) diff --git a/meta-digi-arm/recipes-core/packagegroup/nativesdk-packagegroup-sdk-host.bbappend b/meta-digi-arm/recipes-core/packagegroup/nativesdk-packagegroup-sdk-host.bbappend index 31326324a..1df3246f1 100644 --- a/meta-digi-arm/recipes-core/packagegroup/nativesdk-packagegroup-sdk-host.bbappend +++ b/meta-digi-arm/recipes-core/packagegroup/nativesdk-packagegroup-sdk-host.bbappend @@ -18,6 +18,3 @@ RDEPENDS:${PN} += " \ ${@oe.utils.conditional('DEY_SOC_VENDOR', 'NXP', '${IMX_OPTEE_SDK_RDEPENDS} ${IMX_TRUSTFENCE_SDK_TOOLS}', '', d)} \ ${@oe.utils.conditional('DEY_SOC_VENDOR', 'STM', '${STM_TRUSTFENCE_SDK_TOOLS}', '', d)} \ " - -RDEPENDS:${PN}:append:ccmp25 = " nativesdk-flutter-sdk" -RDEPENDS:${PN}:append:imxgpu = " nativesdk-flutter-sdk" From 724c9492ecab84f8c12852ea16e160e9ca70074c Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Tue, 25 Feb 2025 15:06:20 +0100 Subject: [PATCH 049/138] build: Start a build release environment from scratch The release build should ensure it starts from a fresh environment to avoid potential mismatches with old code. Signed-off-by: Arturo Buzarra --- sdk/build-github.sh | 4 ++++ sdk/build.sh | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/sdk/build-github.sh b/sdk/build-github.sh index be51ffa75..4e42df27c 100755 --- a/sdk/build-github.sh +++ b/sdk/build-github.sh @@ -178,6 +178,10 @@ fi printf "\n[INFO] Build Yocto \"%s\" for \"%s\" (cpus=%s)\n\n" "${DY_REVISION}" "${DY_PLATFORMS}" "${CPUS}" # Install/Update Digi's Yocto SDK +if [ "${DY_BUILD_RELEASE}" = "true" ]; then + # Start a build release environment from scratch + rm -rf "${YOCTO_INST_DIR}" +fi mkdir -p "${YOCTO_INST_DIR}" if pushd "${YOCTO_INST_DIR}"; then # Use git ls-remote to check the revision type diff --git a/sdk/build.sh b/sdk/build.sh index badf6802a..57626cdd4 100755 --- a/sdk/build.sh +++ b/sdk/build.sh @@ -206,6 +206,10 @@ fi printf "\n[INFO] Build Yocto \"%s\" for \"%s\" (cpus=%s)\n\n" "${DY_REVISION}" "${DY_PLATFORMS}" "${CPUS}" # Install/Update Digi's Yocto SDK +if [ "${DY_BUILD_RELEASE}" = "true" ]; then + # Start a build release environment from scratch + rm -rf "${YOCTO_INST_DIR}" +fi mkdir -p "${YOCTO_INST_DIR}" if pushd "${YOCTO_INST_DIR}"; then # Use git ls-remote to check the revision type From 8f1413f5cc165c679c670c793673eaf0d23ed6b0 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Tue, 22 Apr 2025 16:00:41 +0200 Subject: [PATCH 050/138] x-linux-ai: update support for new X-LINUX-AI v6.0.1 release This commit introduces the necessary changes in the Digi Embedded Yocto layer to support the X-LINUX-AI v6.0.1 software package from the meta-st-x-linux-ai layer. The update removes support that is now provided directly by the ST layer in this new version and adapts existing recipes to align with the updated mechanisms for AI application integration. These changes ensure compatibility with platforms that feature hardware acceleration (NPU), as well as those that rely solely on CPU-based inference. Signed-off-by: Arturo Buzarra --- meta-digi-arm/conf/layer.conf | 5 +- ...6.0.0.bbappend => stai-mpu_6.0.1.bbappend} | 0 ...tion-remove-weston-user-check-from-l.patch | 18 +++-- ...-remove-weston-user-check-from-launc.patch | 18 +++-- ...remove-weston-user-check-from-launch.patch | 20 +++--- .../resources/application-resources.bbappend | 7 +- ...-add-support-to-STM32MP255-processor.patch | 62 ---------------- ...-support-for-web-camera-with-STM32MP.patch | 70 +++++++++++++++++++ ...-support-for-web-camera-with-STM32MP.patch | 68 ------------------ ...main_isp-fix-support-for-web-camera.patch} | 0 ..._isp-fix-support-for-CSI-DCMIPP-cam.patch} | 0 ...ation-remove-weston-user-check-from-.patch | 16 +++-- .../packagegroup-dey-x-linux-ai.bb | 30 ++++---- 13 files changed, 135 insertions(+), 179 deletions(-) rename meta-digi-dey/dynamic-layers/x-linux-ai/recipes-frameworks/stai-mpu/{stai-mpu_6.0.0.bbappend => stai-mpu_6.0.1.bbappend} (100%) delete mode 100644 meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0001-config_board-add-support-to-STM32MP255-processor.patch create mode 100644 meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0001-config_board-fix-support-for-web-camera-with-STM32MP.patch delete mode 100644 meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0002-config_board-fix-support-for-web-camera-with-STM32MP.patch rename meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/{0003-setup_camera_main_isp-fix-support-for-web-camera.patch => 0002-setup_camera_main_isp-fix-support-for-web-camera.patch} (100%) rename meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/{0004-setup_camera_main_isp-fix-support-for-CSI-DCMIPP-cam.patch => 0003-setup_camera_main_isp-fix-support-for-CSI-DCMIPP-cam.patch} (100%) diff --git a/meta-digi-arm/conf/layer.conf b/meta-digi-arm/conf/layer.conf index 0ba8036d9..ad128ea0d 100644 --- a/meta-digi-arm/conf/layer.conf +++ b/meta-digi-arm/conf/layer.conf @@ -54,8 +54,9 @@ BBMASK += " \ # Tweak x-linux-ai dependencies to use ST's machine learning packages LAYERDEPENDS_x-linux-ai:remove = "st-openstlinux" BBMASK += " \ - meta-st-x-linux-ai/recipes-st/images/st-image-ai-userfs.bb \ - meta-st-x-linux-ai/recipes-st/images/st-image-ai.bb \ + meta-st-x-linux-ai/recipes-st/images/st-image-ai-npu-userfs.bb \ + meta-st-x-linux-ai/recipes-st/images/st-image-ai-npu.bb \ + meta-st-x-linux-ai/recipes-st/images/st-image-ai-cpu.bb \ " # Digi's General and Open Source license agreements diff --git a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-frameworks/stai-mpu/stai-mpu_6.0.0.bbappend b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-frameworks/stai-mpu/stai-mpu_6.0.1.bbappend similarity index 100% rename from meta-digi-dey/dynamic-layers/x-linux-ai/recipes-frameworks/stai-mpu/stai-mpu_6.0.0.bbappend rename to meta-digi-dey/dynamic-layers/x-linux-ai/recipes-frameworks/stai-mpu/stai-mpu_6.0.1.bbappend diff --git a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/image-classification/files/patches/0001-image-classification-remove-weston-user-check-from-l.patch b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/image-classification/files/patches/0001-image-classification-remove-weston-user-check-from-l.patch index 883b27d94..07953017a 100644 --- a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/image-classification/files/patches/0001-image-classification-remove-weston-user-check-from-l.patch +++ b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/image-classification/files/patches/0001-image-classification-remove-weston-user-check-from-l.patch @@ -10,17 +10,20 @@ Signed-off-by: David Escalona 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/stai-mpu/launch_python_image_classification.sh b/stai-mpu/launch_python_image_classification.sh -index ac5efb5..ab0ed4b 100755 +index 3cb5259..afbedf8 100755 --- a/stai-mpu/launch_python_image_classification.sh +++ b/stai-mpu/launch_python_image_classification.sh -@@ -7,15 +7,9 @@ +@@ -5,18 +5,12 @@ + # + # This software is licensed under terms that can be found in the LICENSE file # in the root directory of this software component. # If no LICENSE file comes with this software, it is provided AS-IS. -weston_user=$(ps aux | grep '/usr/bin/weston '|grep -v 'grep'|awk '{print $1}') FRAMEWORK=$1 echo "stai wrapper used : "$FRAMEWORK - source /usr/local/x-linux-ai/resources/config_board.sh + CONFIG=$(find /usr/local/x-linux-ai -name "config_board_*.sh") + source $CONFIG cmd="python3 /usr/local/x-linux-ai/image-classification/stai_mpu_image_classification.py -m /usr/local/x-linux-ai/image-classification/models/$IMAGE_CLASSIFICATION_MODEL -l /usr/local/x-linux-ai/image-classification/models/$IMAGE_CLASSIFICATION_LABEL.txt --framerate $DFPS --frame_width $DWIDTH --frame_height $DHEIGHT $OPTIONS" -if [ "$weston_user" != "root" ]; then @@ -31,17 +34,20 @@ index ac5efb5..ab0ed4b 100755 -fi +$cmd diff --git a/stai-mpu/launch_python_image_classification_testdata.sh b/stai-mpu/launch_python_image_classification_testdata.sh -index 31ac124..a35f615 100755 +index 25388cd..9d68e90 100755 --- a/stai-mpu/launch_python_image_classification_testdata.sh +++ b/stai-mpu/launch_python_image_classification_testdata.sh -@@ -7,15 +7,9 @@ +@@ -5,18 +5,12 @@ + # + # This software is licensed under terms that can be found in the LICENSE file # in the root directory of this software component. # If no LICENSE file comes with this software, it is provided AS-IS. -weston_user=$(ps aux | grep '/usr/bin/weston '|grep -v 'grep'|awk '{print $1}') FRAMEWORK=$1 echo "stai wrapper used : "$FRAMEWORK - source /usr/local/x-linux-ai/resources/config_board.sh + CONFIG=$(find /usr/local/x-linux-ai -name "config_board_*.sh") + source $CONFIG cmd="python3 /usr/local/x-linux-ai/image-classification/stai_mpu_image_classification.py -m /usr/local/x-linux-ai/image-classification/models/$IMAGE_CLASSIFICATION_MODEL -l /usr/local/x-linux-ai/image-classification/models/$IMAGE_CLASSIFICATION_LABEL.txt -i /usr/local/x-linux-ai/image-classification/models/$IMAGE_CLASSIF_DATA" -if [ "$weston_user" != "root" ]; then diff --git a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/object-detection/files/patches/0001-object-detection-remove-weston-user-check-from-launc.patch b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/object-detection/files/patches/0001-object-detection-remove-weston-user-check-from-launc.patch index d29b3d9e1..aa3a07f8f 100644 --- a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/object-detection/files/patches/0001-object-detection-remove-weston-user-check-from-launc.patch +++ b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/object-detection/files/patches/0001-object-detection-remove-weston-user-check-from-launc.patch @@ -9,17 +9,20 @@ Signed-off-by: David Escalona 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/stai-mpu/launch_python_object_detection.sh b/stai-mpu/launch_python_object_detection.sh -index d0332dc..54f5e54 100755 +index e460fb3..b595f7f 100755 --- a/stai-mpu/launch_python_object_detection.sh +++ b/stai-mpu/launch_python_object_detection.sh -@@ -7,15 +7,9 @@ +@@ -5,18 +5,12 @@ + # + # This software is licensed under terms that can be found in the LICENSE file # in the root directory of this software component. # If no LICENSE file comes with this software, it is provided AS-IS. -weston_user=$(ps aux | grep '/usr/bin/weston '|grep -v 'grep'|awk '{print $1}') FRAMEWORK=$1 echo "stai_mpu wrapper used : "$FRAMEWORK - source /usr/local/x-linux-ai/resources/config_board.sh + CONFIG=$(find /usr/local/x-linux-ai -name "config_board_*.sh") + source $CONFIG cmd="/usr/local/x-linux-ai/object-detection/stai_mpu_object_detection.py -m /usr/local/x-linux-ai/object-detection/models/$OBJ_DETEC_MODEL -l /usr/local/x-linux-ai/object-detection/models/$OBJ_DETEC_MODEL_LABEL.txt --framerate $DFPS --frame_width $DWIDTH --frame_height $DHEIGHT $OPTIONS" -if [ "$weston_user" != "root" ]; then @@ -30,17 +33,20 @@ index d0332dc..54f5e54 100755 -fi +$cmd diff --git a/stai-mpu/launch_python_object_detection_testdata.sh b/stai-mpu/launch_python_object_detection_testdata.sh -index c8e4d5b..bd9a9b9 100755 +index 7c5a6e5..ca547a4 100755 --- a/stai-mpu/launch_python_object_detection_testdata.sh +++ b/stai-mpu/launch_python_object_detection_testdata.sh -@@ -7,15 +7,9 @@ +@@ -5,18 +5,12 @@ + # + # This software is licensed under terms that can be found in the LICENSE file # in the root directory of this software component. # If no LICENSE file comes with this software, it is provided AS-IS. -weston_user=$(ps aux | grep '/usr/bin/weston '|grep -v 'grep'|awk '{print $1}') FRAMEWORK=$1 echo "stai_mpu wrapper used : "$FRAMEWORK - source /usr/local/x-linux-ai/resources/config_board.sh + CONFIG=$(find /usr/local/x-linux-ai -name "config_board_*.sh") + source $CONFIG cmd="/usr/local/x-linux-ai/object-detection/stai_mpu_object_detection.py -m /usr/local/x-linux-ai/object-detection/models/$OBJ_DETEC_MODEL -l /usr/local/x-linux-ai/object-detection/models/$OBJ_DETEC_MODEL_LABEL.txt -i /usr/local/x-linux-ai/object-detection/models/$OBJ_DETECT_DATA" -if [ "$weston_user" != "root" ]; then diff --git a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/pose-estimation/files/patches/0001-pose-estimation-remove-weston-user-check-from-launch.patch b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/pose-estimation/files/patches/0001-pose-estimation-remove-weston-user-check-from-launch.patch index 22b00d4fa..27b56f469 100644 --- a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/pose-estimation/files/patches/0001-pose-estimation-remove-weston-user-check-from-launch.patch +++ b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/pose-estimation/files/patches/0001-pose-estimation-remove-weston-user-check-from-launch.patch @@ -4,12 +4,12 @@ Subject: [PATCH 1/3] pose-estimation: remove weston user check from launch Signed-off-by: David Escalona --- - stai-mpu/launch_python_pose_estimation.sh | 8 +------- - stai-mpu/launch_python_pose_estimation_testdata.sh | 8 +------- + .../files/stai-mpu/launch_python_pose_estimation.sh | 8 +------- + .../stai-mpu/launch_python_pose_estimation_testdata.sh | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/stai-mpu/launch_python_pose_estimation.sh b/stai-mpu/launch_python_pose_estimation.sh -index a317b58..7c41246 100644 +index 61952cf..6c8b5b8 100644 --- a/stai-mpu/launch_python_pose_estimation.sh +++ b/stai-mpu/launch_python_pose_estimation.sh @@ -5,17 +5,11 @@ @@ -17,13 +17,13 @@ index a317b58..7c41246 100644 # This software is licensed under terms that can be found in the LICENSE file # in the root directory of this software component. # If no LICENSE file comes with this software, it is provided AS-IS. - + -weston_user=$(ps aux | grep '/usr/bin/weston '|grep -v 'grep'|awk '{print $1}') FRAMEWORK=$1 echo "stai wrapper used : "$FRAMEWORK - source /usr/local/x-linux-ai/resources/config_board.sh + source /usr/local/x-linux-ai/resources/config_board_npu.sh cmd="python3 /usr/local/x-linux-ai/pose-estimation/stai_mpu_pose_estimation.py -m /usr/local/x-linux-ai/pose-estimation/models/$POSE_ESTIMATION_MODEL --framerate $DFPS --frame_width $DWIDTH --frame_height $DHEIGHT $OPTIONS" - + -if [ "$weston_user" != "root" ]; then - echo "user : "$weston_user - script -qc "su -l $weston_user -c '$cmd'" @@ -33,7 +33,7 @@ index a317b58..7c41246 100644 \ No newline at end of file +$cmd diff --git a/stai-mpu/launch_python_pose_estimation_testdata.sh b/stai-mpu/launch_python_pose_estimation_testdata.sh -index 9992666..cc2f0b6 100644 +index 6ba035b..9a4448e 100644 --- a/stai-mpu/launch_python_pose_estimation_testdata.sh +++ b/stai-mpu/launch_python_pose_estimation_testdata.sh @@ -5,17 +5,11 @@ @@ -41,13 +41,13 @@ index 9992666..cc2f0b6 100644 # This software is licensed under terms that can be found in the LICENSE file # in the root directory of this software component. # If no LICENSE file comes with this software, it is provided AS-IS. - + -weston_user=$(ps aux | grep '/usr/bin/weston '|grep -v 'grep'|awk '{print $1}') FRAMEWORK=$1 echo "stai wrapper used : "$FRAMEWORK - source /usr/local/x-linux-ai/resources/config_board.sh + source /usr/local/x-linux-ai/resources/config_board_npu.sh cmd="python3 /usr/local/x-linux-ai/pose-estimation/stai_mpu_pose_estimation.py -m /usr/local/x-linux-ai/pose-estimation/models/$POSE_ESTIMATION_MODEL -i /usr/local/x-linux-ai/pose-estimation/models/$POSE_ESTIMATION_DATA" - + -if [ "$weston_user" != "root" ]; then - echo "user : "$weston_user - script -qc "su -l $weston_user -c '$cmd'" diff --git a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/application-resources.bbappend b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/application-resources.bbappend index 4db3150ba..bdc044640 100644 --- a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/application-resources.bbappend +++ b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/application-resources.bbappend @@ -3,8 +3,7 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/files:" SRC_URI += " \ - file://patches/0001-config_board-add-support-to-STM32MP255-processor.patch \ - file://patches/0002-config_board-fix-support-for-web-camera-with-STM32MP.patch \ - file://patches/0003-setup_camera_main_isp-fix-support-for-web-camera.patch \ - file://patches/0004-setup_camera_main_isp-fix-support-for-CSI-DCMIPP-cam.patch \ + file://patches/0001-config_board-fix-support-for-web-camera-with-STM32MP.patch \ + file://patches/0002-setup_camera_main_isp-fix-support-for-web-camera.patch \ + file://patches/0003-setup_camera_main_isp-fix-support-for-CSI-DCMIPP-cam.patch \ " diff --git a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0001-config_board-add-support-to-STM32MP255-processor.patch b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0001-config_board-add-support-to-STM32MP255-processor.patch deleted file mode 100644 index 90b0e5724..000000000 --- a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0001-config_board-add-support-to-STM32MP255-processor.patch +++ /dev/null @@ -1,62 +0,0 @@ -From: Arturo Buzarra -Date: Wed, 11 Dec 2024 17:20:22 +0100 -Subject: [PATCH] config_board: add support to STM32MP255 processor - -Signed-off-by: Arturo Buzarra ---- - .../files/resources-files/config_board.sh | 32 +++++++++++++++++++ - 1 file changed, 32 insertions(+) - -diff --git a/resources-files/config_board.sh b/resources-files/config_board.sh -index a9ce24b..a37981a 100644 ---- a/resources-files/config_board.sh -+++ b/resources-files/config_board.sh -@@ -13,6 +13,7 @@ STM32MP2="stm32mp2" - STM32MP135="stm32mp135" - STM32MP157="stm32mp157" - STM32MP157FEV1="stm32mp157f-ev1st" -+STM32MP255="stm32mp255" - STM32MP257="stm32mp257" - STM32MP257FEV1="stm32mp257f-ev1st" - -@@ -79,6 +80,37 @@ if [[ "$COMPATIBLE" == *"$STM32MP157"* ]]; then - fi - fi - -+if [[ "$COMPATIBLE" == *"$STM32MP255"* ]]; then -+ SEMANTIC_SEGMENTATION_MODEL="deeplabv3/deeplabv3_257_int8_per_tensor$NN_EXT" -+ SEMANTIC_SEGMENTATION_LABEL="deeplabv3/labels_pascalvoc" -+ SEMANTIC_SEGMENTATION_DATA="deeplabv3/testdata/" -+ POSE_ESTIMATION_DATA="yolov8n_pose/testdata/" -+ POSE_ESTIMATION_MODEL="yolov8n_pose/yolov8n_256_quant_pt_uf_pose_coco-st$NN_EXT" -+ FACE_DETECTION_MODEL="blazeface/blazeface_128x128_quant$NN_EXT" -+ FACE_DETECTION_DATA="blazeface/testdata/" -+ FACE_RECO_MODEL="facenet/facenet512_160x160_quant$NN_EXT" -+ FACE_RECO_DATA="facenet/testdata/" -+ FACE_DATABASE="database/" -+ # STM32MP255 settings -+ MACHINE=$STM32MP255 -+ DWIDTH=640 -+ DHEIGHT=480 -+ DFPS=30 -+ COMPUTE_ENGINE="--npu" -+ IMAGE_CLASSIFICATION_MODEL="mobilenet/mobilenet_v2_1.0_224_int8_per_tensor$NN_EXT" -+ IMAGE_CLASSIFICATION_LABEL="mobilenet/labels_imagenet_2012" -+ IMAGE_CLASSIF_DATA="mobilenet/testdata/" -+ if [[ "$NN_EXT" == ".nb" ]]; then -+ OBJ_DETEC_MODEL="coco_ssd_mobilenet/ssd_mobilenet_v2_fpnlite_10_256_int8_per_tensor$NN_EXT" -+ OBJ_DETEC_MODEL_LABEL="coco_ssd_mobilenet/labels_coco_dataset_80" -+ else -+ OBJ_DETEC_MODEL="coco_ssd_mobilenet/ssd_mobilenet_v2_fpnlite_10_256_int8$NN_EXT" -+ OBJ_DETEC_MODEL_LABEL="coco_ssd_mobilenet/labels_coco_dataset_80" -+ fi -+ OBJ_DETECT_DATA="coco_ssd_mobilenet/testdata/" -+ OPTIONS="--dual_camera_pipeline" -+fi -+ - if [[ "$COMPATIBLE" == *"$STM32MP257"* ]]; then - SEMANTIC_SEGMENTATION_MODEL="deeplabv3/deeplabv3_257_int8_per_tensor$NN_EXT" - SEMANTIC_SEGMENTATION_LABEL="deeplabv3/labels_pascalvoc" --- -2.47.1 - diff --git a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0001-config_board-fix-support-for-web-camera-with-STM32MP.patch b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0001-config_board-fix-support-for-web-camera-with-STM32MP.patch new file mode 100644 index 000000000..8dea1d258 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0001-config_board-fix-support-for-web-camera-with-STM32MP.patch @@ -0,0 +1,70 @@ +From: Arturo Buzarra +Date: Tue, 22 Apr 2025 14:01:12 +0200 +Subject: [PATCH] config_board: fix support for web camera with STM32MP255 + processor + +This commit disables the dual camera pipeline support for USB cameras, as it is +exclusive to cameras using the DCMIPP peripheral. + +Signed-off-by: Arturo Buzarra +--- + .../files/resources-files/config_board_npu.sh | 29 ++++++++++++++++++- + 1 file changed, 28 insertions(+), 1 deletion(-) + +diff --git a/resources-files/config_board_npu.sh b/resources-files/config_board_npu.sh +index c89b5ca..38dda50 100644 +--- a/resources-files/config_board_npu.sh ++++ b/resources-files/config_board_npu.sh +@@ -16,10 +16,28 @@ STM32MP251="stm32mp251" + STM32MP235="stm32mp235" + STM32MP233="stm32mp233" + STM32MP231="stm32mp231" + STM32MP2_NPU="stm32mp2x with GPU/NPU" + ++function is_dcmipp_present() { ++ DCMIPP_SENSOR="NOTFOUND" ++ # ov5640 or imx335 camera can be present on csi connector ++ for video in $(find /sys/class/video4linux -name "video*" -type l); ++ do ++ if [ "$(cat $video/name)" = "dcmipp_main_capture" ]; then ++ for sub in $(find /sys/class/video4linux -name "v4l-subdev*" -type l); ++ do ++ subdev_name=$(tr -d '\0' < $sub/name | awk '{print $1}') ++ if [ "$subdev_name" = "ov5640" ] || [ "$subdev_name" = "imx335" ]; then ++ DCMIPP_SENSOR=$subdev_name ++ break; ++ fi ++ done ++ fi ++ done ++} ++ + if [[ "$COMPATIBLE" == *"$STM32MP253"* ]] || [[ "$COMPATIBLE" == *"$STM32MP251"* ]] || [[ "$COMPATIBLE" == *"$STM32MP233"* ]] || [[ "$COMPATIBLE" == *"$STM32MP231"* ]]; then + if [[ "$SOFTWARE" == "AI_NPU" ]]; then + echo "Software X-LINUX-AI installed is not compatible with the board, please install X-LINUX-AI CPU version for plateform without hardware accelerator" + exit 1 + fi +@@ -54,11 +72,20 @@ if [[ "$COMPATIBLE" == *"$STM32MP257"* ]] || [[ "$COMPATIBLE" == *"$STM32MP255"* + MACHINE=$STM32MP2_NPU + DWIDTH=760 + DHEIGHT=568 + DFPS=30 + COMPUTE_ENGINE="--npu" +- OPTIONS="--dual_camera_pipeline" ++ ++ is_dcmipp_present ++ if [ "$DCMIPP_SENSOR" != "NOTFOUND" ]; then ++ # DCMIPP camera ++ OPTIONS="--dual_camera_pipeline" ++ else ++ # Web camera ++ OPTIONS="" ++ fi ++ + IMAGE_CLASSIFICATION_MODEL="mobilenet/mobilenet_v2_1.0_224_int8_per_tensor$NN_EXT" + IMAGE_CLASSIFICATION_LABEL="mobilenet/labels_imagenet_2012" + IMAGE_CLASSIF_DATA="mobilenet/testdata/" + if [[ "$NN_EXT" == ".nb" ]]; then + OBJ_DETEC_MODEL="coco_ssd_mobilenet/ssd_mobilenet_v2_fpnlite_10_256_int8_per_tensor$NN_EXT" +-- +2.34.1 diff --git a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0002-config_board-fix-support-for-web-camera-with-STM32MP.patch b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0002-config_board-fix-support-for-web-camera-with-STM32MP.patch deleted file mode 100644 index f101e2873..000000000 --- a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0002-config_board-fix-support-for-web-camera-with-STM32MP.patch +++ /dev/null @@ -1,68 +0,0 @@ -From: Arturo Buzarra -Date: Tue, 21 Jan 2025 12:04:52 +0100 -Subject: [PATCH] config_board: fix support for web camera with STM32MP255 - processor - -This commit disables the dual camera pipeline support for USB cameras, as it is -exclusive to cameras using the DCMIPP peripheral. - -Signed-off-by: Arturo Buzarra ---- - .../files/resources-files/config_board.sh | 27 +++++++++++++++++++ - 1 file changed, 27 insertions(+) - -diff --git a/resources-files/config_board.sh b/resources-files/config_board.sh -index a623086..e7e8b00 100644 ---- a/resources-files/config_board.sh -+++ b/resources-files/config_board.sh -@@ -15,10 +15,28 @@ STM32MP157="stm32mp157" - STM32MP157FEV1="stm32mp157f-ev1st" - STM32MP255="stm32mp255" - STM32MP257="stm32mp257" - STM32MP257FEV1="stm32mp257f-ev1st" - -+function is_dcmipp_present() { -+ DCMIPP_SENSOR="NOTFOUND" -+ # ov5640 or imx335 camera can be present on csi connector -+ for video in $(find /sys/class/video4linux -name "video*" -type l); -+ do -+ if [ "$(cat $video/name)" = "dcmipp_main_capture" ]; then -+ for sub in $(find /sys/class/video4linux -name "v4l-subdev*" -type l); -+ do -+ subdev_name=$(tr -d '\0' < $sub/name | awk '{print $1}') -+ if [ "$subdev_name" = "ov5640" ] || [ "$subdev_name" = "imx335" ]; then -+ DCMIPP_SENSOR=$subdev_name -+ break; -+ fi -+ done -+ fi -+ done -+} -+ - if [[ "$FRAMEWORK" == "nbg" ]]; then - NN_EXT=".nb" - elif [[ "$FRAMEWORK" == "tflite" ]]; then - NN_EXT=".tflite" - elif [[ "$FRAMEWORK" == "onnx" ]]; then -@@ -107,10 +125,19 @@ if [[ "$COMPATIBLE" == *"$STM32MP255"* ]]; then - OBJ_DETEC_MODEL="coco_ssd_mobilenet/ssd_mobilenet_v2_fpnlite_10_256_int8$NN_EXT" - OBJ_DETEC_MODEL_LABEL="coco_ssd_mobilenet/labels_coco_dataset_80" - fi - OBJ_DETECT_DATA="coco_ssd_mobilenet/testdata/" - OPTIONS="--dual_camera_pipeline" -+ -+ is_dcmipp_present -+ if [ "$DCMIPP_SENSOR" != "NOTFOUND" ]; then -+ # DCMIPP camera -+ OPTIONS="--dual_camera_pipeline" -+ else -+ # Web camera -+ OPTIONS="" -+ fi - fi - - if [[ "$COMPATIBLE" == *"$STM32MP257"* ]]; then - SEMANTIC_SEGMENTATION_MODEL="deeplabv3/deeplabv3_257_int8_per_tensor$NN_EXT" - SEMANTIC_SEGMENTATION_LABEL="deeplabv3/labels_pascalvoc" --- -2.34.1 diff --git a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0003-setup_camera_main_isp-fix-support-for-web-camera.patch b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0002-setup_camera_main_isp-fix-support-for-web-camera.patch similarity index 100% rename from meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0003-setup_camera_main_isp-fix-support-for-web-camera.patch rename to meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0002-setup_camera_main_isp-fix-support-for-web-camera.patch diff --git a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0004-setup_camera_main_isp-fix-support-for-CSI-DCMIPP-cam.patch b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0003-setup_camera_main_isp-fix-support-for-CSI-DCMIPP-cam.patch similarity index 100% rename from meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0004-setup_camera_main_isp-fix-support-for-CSI-DCMIPP-cam.patch rename to meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/resources/files/patches/0003-setup_camera_main_isp-fix-support-for-CSI-DCMIPP-cam.patch diff --git a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/semantic-segmentation/files/patches/0001-semantic-segmentation-remove-weston-user-check-from-.patch b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/semantic-segmentation/files/patches/0001-semantic-segmentation-remove-weston-user-check-from-.patch index b94de0be2..8910c2339 100644 --- a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/semantic-segmentation/files/patches/0001-semantic-segmentation-remove-weston-user-check-from-.patch +++ b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-samples/semantic-segmentation/files/patches/0001-semantic-segmentation-remove-weston-user-check-from-.patch @@ -10,17 +10,19 @@ Signed-off-by: David Escalona 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/stai-mpu/launch_python_semantic_segmentation.sh b/stai-mpu/launch_python_semantic_segmentation.sh -index e3c3a6c..e008572 100755 +index a1c2479..94cd540 100755 --- a/stai-mpu/launch_python_semantic_segmentation.sh +++ b/stai-mpu/launch_python_semantic_segmentation.sh -@@ -7,15 +7,9 @@ +@@ -5,17 +5,11 @@ + # + # This software is licensed under terms that can be found in the LICENSE file # in the root directory of this software component. # If no LICENSE file comes with this software, it is provided AS-IS. -weston_user=$(ps aux | grep '/usr/bin/weston '|grep -v 'grep'|awk '{print $1}') FRAMEWORK=$1 echo "stai-mpu wrapper used : "$FRAMEWORK - source /usr/local/x-linux-ai/resources/config_board.sh + source /usr/local/x-linux-ai/resources/config_board_npu.sh cmd="python3 /usr/local/x-linux-ai/semantic-segmentation/stai_mpu_semantic_segmentation.py -m /usr/local/x-linux-ai/semantic-segmentation/models/$SEMANTIC_SEGMENTATION_MODEL -l /usr/local/x-linux-ai/semantic-segmentation/models/$SEMANTIC_SEGMENTATION_LABEL.txt --framerate $DFPS --frame_width $DWIDTH --frame_height $DHEIGHT $OPTIONS" -if [ "$weston_user" != "root" ]; then @@ -31,17 +33,19 @@ index e3c3a6c..e008572 100755 -fi +$cmd diff --git a/stai-mpu/launch_python_semantic_segmentation_testdata.sh b/stai-mpu/launch_python_semantic_segmentation_testdata.sh -index 2d3db4b..5375e0f 100755 +index bf3ac5e..a02de8f 100755 --- a/stai-mpu/launch_python_semantic_segmentation_testdata.sh +++ b/stai-mpu/launch_python_semantic_segmentation_testdata.sh -@@ -7,15 +7,9 @@ +@@ -5,17 +5,11 @@ + # + # This software is licensed under terms that can be found in the LICENSE file # in the root directory of this software component. # If no LICENSE file comes with this software, it is provided AS-IS. -weston_user=$(ps aux | grep '/usr/bin/weston '|grep -v 'grep'|awk '{print $1}') FRAMEWORK=$1 echo "stai-mpu wrapper used : "$FRAMEWORK - source /usr/local/x-linux-ai/resources/config_board.sh + source /usr/local/x-linux-ai/resources/config_board_npu.sh cmd="python3 /usr/local/x-linux-ai/semantic-segmentation/stai_mpu_semantic_segmentation.py -m /usr/local/x-linux-ai/semantic-segmentation/models/$SEMANTIC_SEGMENTATION_MODEL -l /usr/local/x-linux-ai/semantic-segmentation/models/$SEMANTIC_SEGMENTATION_LABEL.txt -i /usr/local/x-linux-ai/semantic-segmentation/models/$SEMANTIC_SEGMENTATION_DATA" -if [ "$weston_user" != "root" ]; then diff --git a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-st/packagegroups/packagegroup-dey-x-linux-ai.bb b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-st/packagegroups/packagegroup-dey-x-linux-ai.bb index dd096fe12..a7ce9a6d4 100644 --- a/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-st/packagegroups/packagegroup-dey-x-linux-ai.bb +++ b/meta-digi-dey/dynamic-layers/x-linux-ai/recipes-st/packagegroups/packagegroup-dey-x-linux-ai.bb @@ -11,30 +11,30 @@ COMMON_PACKAGES = " \ " TFLITE_PACKAGES = " \ - stai-mpu-image-classification-cpp-tfl \ - stai-mpu-image-classification-python-tfl \ - stai-mpu-object-detection-cpp-tfl \ - stai-mpu-object-detection-python-tfl \ + stai-mpu-image-classification-cpp-tfl-npu \ + stai-mpu-image-classification-python-tfl-npu \ + stai-mpu-object-detection-cpp-tfl-npu \ + stai-mpu-object-detection-python-tfl-npu \ tflite-vx-delegate-example \ " ONNX_PACKAGES = " \ onnxruntime-tools \ - stai-mpu-image-classification-cpp-ort \ - stai-mpu-image-classification-python-ort \ - stai-mpu-object-detection-python-ort \ - stai-mpu-object-detection-cpp-ort \ + stai-mpu-image-classification-cpp-ort-npu \ + stai-mpu-image-classification-python-ort-npu \ + stai-mpu-object-detection-python-ort-npu \ + stai-mpu-object-detection-cpp-ort-npu \ " OPENVX_PACKAGES = " \ nbg-benchmark \ - stai-mpu-image-classification-cpp-ovx \ - stai-mpu-image-classification-python-ovx \ - stai-mpu-object-detection-cpp-ovx \ - stai-mpu-object-detection-python-ovx \ - stai-mpu-semantic-segmentation-python-ovx \ - stai-mpu-pose-estimation-python-ovx \ - stai-mpu-face-recognition-cpp-ovx \ + stai-mpu-image-classification-cpp-ovx-npu \ + stai-mpu-image-classification-python-ovx-npu \ + stai-mpu-object-detection-cpp-ovx-npu \ + stai-mpu-object-detection-python-ovx-npu \ + stai-mpu-semantic-segmentation-python-ovx-npu \ + stai-mpu-pose-estimation-python-ovx-npu \ + stai-mpu-face-recognition-cpp-ovx-npu \ " RDEPENDS:${PN} += " \ From 31b7c5e051aa7315cd405063fe89ac8d18aaaaf5 Mon Sep 17 00:00:00 2001 From: Isaac Hermida Date: Tue, 22 Apr 2025 12:32:19 +0200 Subject: [PATCH 051/138] linux-virtualization: add more defconfig options for docker Enable CONFIG_BPF_SYSCALL and CONFIG_CGROUP_BPF when using docker, as it is needed for a correct network interoperability. Some kernels may have it enabled by default, but others not, so just enable it for any platform. https://onedigi.atlassian.net/browse/DEL-9592 Signed-off-by: Isaac Hermida --- meta-digi-arm/recipes-kernel/linux/linux-dey/docker_conf.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey/docker_conf.cfg b/meta-digi-arm/recipes-kernel/linux/linux-dey/docker_conf.cfg index bcfdb4c0c..686f2a30f 100644 --- a/meta-digi-arm/recipes-kernel/linux/linux-dey/docker_conf.cfg +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey/docker_conf.cfg @@ -62,3 +62,5 @@ CONFIG_NETLINK_DIAG=m CONFIG_MACVLAN=y CONFIG_BTRFS_FS=y CONFIG_BTRFS_FS_POSIX_ACL=y +CONFIG_BPF_SYSCALL=y +CONFIG_CGROUP_BPF=y From 8b8788d68e685f0de49f967b40d9afc875bbe57f Mon Sep 17 00:00:00 2001 From: Gabriel Valcazar Date: Wed, 23 Apr 2025 14:00:25 +0200 Subject: [PATCH 052/138] qt6: qttools: remove spurious clang dependency in dey-toolchain SDK Typically, recipes that support clang builds only pull in the required clang dependencies when the TOOLCHAIN variable is expliciltly set to "clang". The qttools recipe in meta-qt6 differs from this by adding the dependency as soon as meta-clang is present in the bblayers, regardless of the selected toolchain. This causes all of the nativesdk clang packages to get included in our SDK (along with the gcc ones), causing SDK bloat and spurious build errors on build machines with limited resources. Make sure that the dependency is only added when the clang TOOLCHAIN is explicitly selected. Signed-off-by: Gabriel Valcazar --- .../qt6-layer/recipes-qt/qt6/qttools_git.bbappend | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 meta-digi-dey/dynamic-layers/qt6-layer/recipes-qt/qt6/qttools_git.bbappend diff --git a/meta-digi-dey/dynamic-layers/qt6-layer/recipes-qt/qt6/qttools_git.bbappend b/meta-digi-dey/dynamic-layers/qt6-layer/recipes-qt/qt6/qttools_git.bbappend new file mode 100644 index 000000000..3a59ef059 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/qt6-layer/recipes-qt/qt6/qttools_git.bbappend @@ -0,0 +1,10 @@ +# Copyright (C) 2025, Digi International Inc. + +# The native and nativesdk versions of this recipe automatically pull in a +# clang dependency as soon as meta-clang is included in the bblayers. Change +# this so that the dependency only gets added when explicitly setting TOOLCHAIN +# to clang. +QTTOOLS_USE_CLANG = "" + +# This check is copied as-is from meta-qt6 +QTTOOLS_USE_CLANG:toolchain-clang = "${@ 'clang' if bb.utils.vercmp_string_op(d.getVar('LLVMVERSION') or '', '17', '>') else ''}" From e09eff7e1aa5e2b1b28d73c106d0b7cc9e17bf15 Mon Sep 17 00:00:00 2001 From: Gabriel Valcazar Date: Fri, 11 Apr 2025 11:36:22 +0200 Subject: [PATCH 053/138] stm32mp: gstreamer1.0-plugins-bad: revert STM patch to fix .flv video playback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit STM patch nº8 was causing memory mapping errors when playing .flv videos on the ccmp25-dvk, so revert it. https://onedigi.atlassian.net/browse/DEL-9466 Signed-off-by: Gabriel Valcazar --- ...abuf-Handle-video-meta-inside-the-im.patch | 189 ++++++++++++++++++ .../gstreamer1.0-plugins-bad_1.22.12.bbappend | 5 + 2 files changed, 194 insertions(+) create mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/Revert-wllinuxdmabuf-Handle-video-meta-inside-the-im.patch diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/Revert-wllinuxdmabuf-Handle-video-meta-inside-the-im.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/Revert-wllinuxdmabuf-Handle-video-meta-inside-the-im.patch new file mode 100644 index 000000000..86b0ea42f --- /dev/null +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/Revert-wllinuxdmabuf-Handle-video-meta-inside-the-im.patch @@ -0,0 +1,189 @@ +From: Gabriel Valcazar +Date: Fri, 11 Apr 2025 10:33:50 +0200 +Subject: [PATCH] Revert "wllinuxdmabuf: Handle video meta inside the importer" + +This commit is causing errors when playing .flv and .ogv files. Other formats +work as expected when reverting it + +Upstream-Status: Inappropriate [DEY specific] + +Signed-off-by: Gabriel Valcazar +--- + ext/gtk/gstgtkwaylandsink.c | 24 +++++++++++++++++++----- + ext/wayland/gstwaylandsink.c | 20 +++++++++++++++++--- + gst-libs/gst/wayland/gstwllinuxdmabuf.c | 13 ++----------- + 3 files changed, 38 insertions(+), 19 deletions(-) + +diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c +index 758cbfe..1396afe 100644 +--- a/ext/gtk/gstgtkwaylandsink.c ++++ b/ext/gtk/gstgtkwaylandsink.c +@@ -1156,7 +1156,9 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + gst_gtk_wayland_sink_get_instance_private (self); + GstBuffer *to_render; + GstWlBuffer *wlbuffer; ++ GstVideoMeta *vmeta; + GstVideoFormat format; ++ GstVideoInfo src_vinfo; + GstMemory *mem; + struct wl_buffer *wbuf = NULL; + +@@ -1199,11 +1201,23 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + /* update video info from video meta */ + mem = gst_buffer_peek_memory (buffer, 0); + ++ src_vinfo = priv->video_info; ++ vmeta = gst_buffer_get_video_meta (buffer); ++ if (vmeta) { ++ gint i; ++ ++ for (i = 0; i < vmeta->n_planes; i++) { ++ src_vinfo.offset[i] = vmeta->offset[i]; ++ src_vinfo.stride[i] = vmeta->stride[i]; ++ } ++ src_vinfo.size = gst_buffer_get_size (buffer); ++ } ++ + GST_LOG_OBJECT (self, + "buffer %" GST_PTR_FORMAT " does not have a wl_buffer from our " + "display, creating it", buffer); + +- format = GST_VIDEO_INFO_FORMAT (&priv->video_info); ++ format = GST_VIDEO_INFO_FORMAT (&src_vinfo); + if (gst_wl_display_check_format_for_dmabuf (priv->display, format)) { + guint i, nb_dmabuf = 0; + +@@ -1213,7 +1227,7 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + + if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer))) + wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, priv->display, +- &priv->video_info); ++ &src_vinfo); + + if (wbuf) + GST_LOG_OBJECT (self, +@@ -1257,7 +1271,7 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + GST_MAP_WRITE)) + goto dst_map_failed; + +- if (!gst_video_frame_map (&src, &priv->video_info, buffer, GST_MAP_READ)) { ++ if (!gst_video_frame_map (&src, &src_vinfo, buffer, GST_MAP_READ)) { + gst_video_frame_unmap (&dst); + goto src_map_failed; + } +@@ -1275,7 +1289,7 @@ handle_shm: + if (!wbuf && gst_wl_display_check_format_for_shm (priv->display, format)) { + if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (mem)) + wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, priv->display, +- &priv->video_info); ++ &src_vinfo); + + /* If nothing worked, copy into our internal pool */ + if (!wbuf) { +@@ -1316,7 +1330,7 @@ handle_shm: + GST_MAP_WRITE)) + goto dst_map_failed; + +- if (!gst_video_frame_map (&src, &priv->video_info, buffer, GST_MAP_READ)) { ++ if (!gst_video_frame_map (&src, &src_vinfo, buffer, GST_MAP_READ)) { + gst_video_frame_unmap (&dst); + goto src_map_failed; + } +diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c +index c93fec9..b557ade 100644 +--- a/ext/wayland/gstwaylandsink.c ++++ b/ext/wayland/gstwaylandsink.c +@@ -862,7 +862,9 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + GstWaylandSink *self = GST_WAYLAND_SINK (vsink); + GstBuffer *to_render; + GstWlBuffer *wlbuffer; ++ GstVideoMeta *vmeta; + GstVideoFormat format; ++ GstVideoInfo src_vinfo; + GstMemory *mem; + struct wl_buffer *wbuf = NULL; + +@@ -916,6 +918,18 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + /* update video info from video meta */ + mem = gst_buffer_peek_memory (buffer, 0); + ++ src_vinfo = self->video_info; ++ vmeta = gst_buffer_get_video_meta (buffer); ++ if (vmeta) { ++ gint i; ++ ++ for (i = 0; i < vmeta->n_planes; i++) { ++ src_vinfo.offset[i] = vmeta->offset[i]; ++ src_vinfo.stride[i] = vmeta->stride[i]; ++ } ++ src_vinfo.size = gst_buffer_get_size (buffer); ++ } ++ + GST_LOG_OBJECT (self, + "buffer %" GST_PTR_FORMAT " does not have a wl_buffer from our " + "display, creating it", buffer); +@@ -930,7 +944,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + + if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer))) + wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, self->display, +- &self->video_info); ++ &src_vinfo); + + if (wbuf) + GST_LOG_OBJECT (self, +@@ -974,7 +988,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) + GST_MAP_WRITE)) + goto dst_map_failed; + +- if (!gst_video_frame_map (&src, &self->video_info, buffer, GST_MAP_READ)) { ++ if (!gst_video_frame_map (&src, &src_vinfo, buffer, GST_MAP_READ)) { + gst_video_frame_unmap (&dst); + goto src_map_failed; + } +@@ -1032,7 +1046,7 @@ handle_shm: + GST_MAP_WRITE)) + goto dst_map_failed; + +- if (!gst_video_frame_map (&src, &self->video_info, buffer, GST_MAP_READ)) { ++ if (!gst_video_frame_map (&src, &src_vinfo, buffer, GST_MAP_READ)) { + gst_video_frame_unmap (&dst); + goto src_map_failed; + } +diff --git a/gst-libs/gst/wayland/gstwllinuxdmabuf.c b/gst-libs/gst/wayland/gstwllinuxdmabuf.c +index deb5d32..16b8fc1 100644 +--- a/gst-libs/gst/wayland/gstwllinuxdmabuf.c ++++ b/gst-libs/gst/wayland/gstwllinuxdmabuf.c +@@ -88,9 +88,6 @@ gst_wl_linux_dmabuf_construct_wl_buffer (GstBuffer * buf, + GstMemory *mem; + int format; + guint i, width, height; +- const gsize *offsets = info->offset; +- const gint *strides = info->stride; +- GstVideoMeta *vmeta; + guint nplanes, flags = 0; + struct zwp_linux_buffer_params_v1 *params; + gint64 timeout; +@@ -110,12 +107,6 @@ gst_wl_linux_dmabuf_construct_wl_buffer (GstBuffer * buf, + height = GST_VIDEO_INFO_HEIGHT (info); + nplanes = GST_VIDEO_INFO_N_PLANES (info); + +- vmeta = gst_buffer_get_video_meta (buf); +- if (vmeta) { +- offsets = vmeta->offset; +- strides = vmeta->stride; +- } +- + GST_DEBUG_OBJECT (display, "Creating wl_buffer from DMABuf of size %" + G_GSSIZE_FORMAT " (%d x %d), format %s", info->size, width, height, + gst_wl_dmabuf_format_to_string (format)); +@@ -128,8 +119,8 @@ gst_wl_linux_dmabuf_construct_wl_buffer (GstBuffer * buf, + guint offset, stride, mem_idx, length; + gsize skip; + +- offset = offsets[i]; +- stride = strides[i]; ++ offset = GST_VIDEO_INFO_PLANE_OFFSET (info, i); ++ stride = GST_VIDEO_INFO_PLANE_STRIDE (info, i); + if (gst_buffer_find_memory (buf, offset, 1, &mem_idx, &length, &skip)) { + GstMemory *m = gst_buffer_peek_memory (buf, mem_idx); + gint fd = gst_dmabuf_memory_get_fd (m); diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend index 95a873c4e..b300c1e42 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend @@ -101,3 +101,8 @@ do_install:append() { install -d ${D}${includedir}/gstreamer-1.0/wayland install -m 644 ${S}/gst-libs/gst/wayland/wayland.h ${D}${includedir}/gstreamer-1.0/wayland } + +# DEY: revert STM patch nº8 to fix .flv video playback error +SRC_URI:append = " \ + file://Revert-wllinuxdmabuf-Handle-video-meta-inside-the-im.patch \ +" From 499ce2e97b7ac1dbf6a25ee7e6c83b6e149b0484 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Thu, 24 Apr 2025 13:27:26 +0200 Subject: [PATCH 054/138] flutter-apps-layer: add init service to automatically launch Flutter demo This commit introduces a dynamic layer that extends the flutter-apps layer by adding an init service to automatically launch the Flutter demo on boot. https://onedigi.atlassian.net/browse/DEL-9380 Signed-off-by: Arturo Buzarra --- meta-digi-dey/conf/layer.conf | 2 + .../first-party/files/flutter-demo-init | 111 ++++++++++++++++++ .../files/flutter-demo-init.service | 11 ++ .../flutter-samples-veggieseasons_%.bbappend | 47 ++++++++ .../packagegroups/packagegroup-dey-flutter.bb | 1 + 5 files changed, 172 insertions(+) create mode 100644 meta-digi-dey/dynamic-layers/flutter-apps-layer/recipes-graphics/flutter-apps/first-party/files/flutter-demo-init create mode 100644 meta-digi-dey/dynamic-layers/flutter-apps-layer/recipes-graphics/flutter-apps/first-party/files/flutter-demo-init.service create mode 100644 meta-digi-dey/dynamic-layers/flutter-apps-layer/recipes-graphics/flutter-apps/first-party/flutter-samples-veggieseasons_%.bbappend diff --git a/meta-digi-dey/conf/layer.conf b/meta-digi-dey/conf/layer.conf index caf82bd6a..24bfa794e 100644 --- a/meta-digi-dey/conf/layer.conf +++ b/meta-digi-dey/conf/layer.conf @@ -28,6 +28,8 @@ BBFILES_DYNAMIC += " \ qt6-layer:${LAYERDIR}/dynamic-layers/qt6-layer/*/*/*.bbappend \ qt6-layer:${LAYERDIR}/dynamic-layers/qt6-layer/${DEY_SOC_VENDOR}/*/*/*.bb \ qt6-layer:${LAYERDIR}/dynamic-layers/qt6-layer/${DEY_SOC_VENDOR}/*/*/*.bbappend \ + flutter-apps-layer:${LAYERDIR}/dynamic-layers/flutter-apps-layer/*/*/*/*.bb \ + flutter-apps-layer:${LAYERDIR}/dynamic-layers/flutter-apps-layer/*/*/*/*.bbappend \ " BBFILE_COLLECTIONS += "digi-dey" diff --git a/meta-digi-dey/dynamic-layers/flutter-apps-layer/recipes-graphics/flutter-apps/first-party/files/flutter-demo-init b/meta-digi-dey/dynamic-layers/flutter-apps-layer/recipes-graphics/flutter-apps/first-party/files/flutter-demo-init new file mode 100644 index 000000000..a15331c36 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/flutter-apps-layer/recipes-graphics/flutter-apps/first-party/files/flutter-demo-init @@ -0,0 +1,111 @@ +#!/bin/sh +#=============================================================================== +# +# Copyright (C) 2025 by Digi International Inc. +# All rights reserved. +# +# 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. +# +# +# !Description: Initialize Flutter demo +# +#=============================================================================== + +readonly DEMO_NAME="##FLUTTER_DEMO_NAME##" +readonly FLUTTER_VERSION="##FLUTTER_SDK_TAG##" +readonly FLUTTER_MODE="##FLUTTER_MODE##" +readonly DEMO_PATH="/usr/share/flutter/${DEMO_NAME}/${FLUTTER_VERSION}/${FLUTTER_MODE}/" +readonly DEMO_TITLE="Flutter Demo Application" +readonly PID_FILE="/run/${DEMO_NAME}.pid" + +log() { + if type "systemd-cat" >/dev/null 2>/dev/null; then + systemd-cat -p "${1}" -t "${DEMO_NAME}" printf "%s" "${2}" + fi + logger -p "${1}" -t "${DEMO_NAME}" "${2}" +} + +get_demo_pid() { + local pid="$(pgrep -f ${DEMO_PATH})" + + [ -n "${pid}" ] && { echo "${pid}"; return 0; } + + return 1 +} + +check_is_running() { + local pid + + if [ -s "${PID_FILE}" ]; then + pid="$(cat ${PID_FILE})" + else + pid="$(get_demo_pid)" + echo "${pid}" > ${PID_FILE} + fi + + if [ "${pid}" ]; then + kill -0 "${pid}" >/dev/null 2>&1 && return 0 + fi + + rm -f "${PID_FILE}" + + return 1 +} + +stop() { + check_is_running || return + + local pid="$(cat ${PID_FILE})" + kill -TERM "${pid}" >/dev/null 2>&1 + + local STOP_TIMEOUT="5" + for i in $(seq ${STOP_TIMEOUT}); do + check_is_running || { log info "stopped"; break; } + if [ "${i}" -eq ${STOP_TIMEOUT} ]; then + log warning "stop: ${DEMO_NAME} did not stop gracefully" + kill -KILL "${pid}" >/dev/null 2>&1 + fi + sleep 1 + done +} + +start() { + check_is_running && { log warning "start: ${DEMO_NAME} ALREADY running"; exit 0; } + + # Launch demo + flutter-pi --release ${DEMO_PATH} >/dev/null 2>&1 & + + if [ $? -eq 0 ]; then + echo $! > ${PID_FILE} + log info "$(cat ${PID_FILE})" + log info "started" + fi +} + +case "$1" in + start) + echo -n "Starting ${DEMO_TITLE}: " + start + echo "done." + ;; + stop) + stop + echo -n "Stopping ${DEMO_TITLE}: " + if [ -n "`/bin/pidof ${DEMO_PATH}`" ] ; then + echo "FAIL" + else + echo "OK" + fi + ;; + restart) + stop + sleep 1 + start + ;; + *) + echo "Usage: $0 {start|stop|restart}" + exit 1 + ;; +esac diff --git a/meta-digi-dey/dynamic-layers/flutter-apps-layer/recipes-graphics/flutter-apps/first-party/files/flutter-demo-init.service b/meta-digi-dey/dynamic-layers/flutter-apps-layer/recipes-graphics/flutter-apps/first-party/files/flutter-demo-init.service new file mode 100644 index 000000000..c8f2efda7 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/flutter-apps-layer/recipes-graphics/flutter-apps/first-party/files/flutter-demo-init.service @@ -0,0 +1,11 @@ +[Unit] +Description=Flutter Demo Application + +[Service] +Type=forking +PIDFile=/run/##FLUTTER_DEMO_NAME##.pid +ExecStart=/etc/flutter-demo-init start +ExecStop=/etc/flutter-demo-init stop + +[Install] +WantedBy=multi-user.target diff --git a/meta-digi-dey/dynamic-layers/flutter-apps-layer/recipes-graphics/flutter-apps/first-party/flutter-samples-veggieseasons_%.bbappend b/meta-digi-dey/dynamic-layers/flutter-apps-layer/recipes-graphics/flutter-apps/first-party/flutter-samples-veggieseasons_%.bbappend new file mode 100644 index 000000000..090e8d716 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/flutter-apps-layer/recipes-graphics/flutter-apps/first-party/flutter-samples-veggieseasons_%.bbappend @@ -0,0 +1,47 @@ +# Copyright (C) 2025, Digi International Inc. + +FILESEXTRAPATHS:prepend:dey := "${THISDIR}/files:" + +SRC_URI:append = " \ + file://flutter-demo-init \ + file://flutter-demo-init.service \ +" + +FLUTTER_DEMO_NAME ?= "${PN}" +FLUTTER_MODE ?= "${FLUTTER_APP_RUNTIME_MODES}" + +inherit update-rc.d systemd + +do_install:append() { + # Install systemd service + if ${@bb.utils.contains('DISTRO_FEATURES', 'systemd', 'true', 'false', d)}; then + # Install systemd unit files + install -d ${D}${systemd_unitdir}/system + install -m 0644 ${WORKDIR}/flutter-demo-init.service ${D}${systemd_unitdir}/system/ + sed -i -e "s,##FLUTTER_DEMO_NAME##,${FLUTTER_DEMO_NAME},g" \ + "${D}${systemd_unitdir}/system/flutter-demo-init.service" + fi + + # Install wrapper bootscript to launch Flutter demo on boot + install -d ${D}${sysconfdir}/init.d + install -m 0755 ${WORKDIR}/flutter-demo-init ${D}${sysconfdir}/flutter-demo-init + ln -sf ${sysconfdir}/flutter-demo-init ${D}${sysconfdir}/init.d/flutter-demo-init + sed -i -e "s@##FLUTTER_SDK_TAG##@${FLUTTER_SDK_TAG}@g" \ + -e "s@##FLUTTER_MODE##@${FLUTTER_MODE}@g" \ + -e "s@##FLUTTER_DEMO_NAME##@${FLUTTER_DEMO_NAME}@g" \ + "${D}${sysconfdir}/flutter-demo-init" +} + +PACKAGES =+ "${PN}-init" +FILES:${PN}-init = " \ + ${sysconfdir}/flutter-demo-init \ + ${sysconfdir}/init.d/flutter-demo-init \ + ${systemd_unitdir}/system/flutter-demo-init.service \ +" + +INITSCRIPT_PACKAGES += "${PN}-init" +INITSCRIPT_NAME:${PN}-init = "flutter-demo-init" +INITSCRIPT_PARAMS:${PN}-init = "start 99 3 5 . stop 20 0 1 2 6 ." + +SYSTEMD_PACKAGES = "${PN}-init" +SYSTEMD_SERVICE:${PN}-init = "flutter-demo-init.service" diff --git a/meta-digi-dey/recipes-graphics/packagegroups/packagegroup-dey-flutter.bb b/meta-digi-dey/recipes-graphics/packagegroups/packagegroup-dey-flutter.bb index 1d8a1f582..8b0204cf5 100644 --- a/meta-digi-dey/recipes-graphics/packagegroups/packagegroup-dey-flutter.bb +++ b/meta-digi-dey/recipes-graphics/packagegroups/packagegroup-dey-flutter.bb @@ -9,6 +9,7 @@ inherit packagegroup RDEPENDS:${PN} += " \ flutter-pi \ flutter-samples-veggieseasons \ + flutter-samples-veggieseasons-init \ " RDEPENDS:${PN}:append:imxgpu = " \ From 3b6e17274aa8ac1de5d12481523b17e73569644a Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Thu, 24 Apr 2025 17:01:39 +0200 Subject: [PATCH 055/138] trustfence-fscrypt: update tarball based on libteec v1.0.0 This commit updates the tarball to be based on libteec v1.0.0, as the previous version was generated without the correct Trusted Application (TA) and did not work properly. Signed-off-by: Arturo Buzarra --- .../recipes-core/trustfence/trustfence-fscrypt_0.2.bb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meta-digi-dey/recipes-core/trustfence/trustfence-fscrypt_0.2.bb b/meta-digi-dey/recipes-core/trustfence/trustfence-fscrypt_0.2.bb index e7eebf779..5d957ca9d 100644 --- a/meta-digi-dey/recipes-core/trustfence/trustfence-fscrypt_0.2.bb +++ b/meta-digi-dey/recipes-core/trustfence/trustfence-fscrypt_0.2.bb @@ -15,8 +15,8 @@ SRC_URI[arm.sha256sum] = "7dee4bbcff21d817bbbc152e904e8091362378446b08ad2d485f37 # Trustfence fscrypt based on libteec v1.0.0 SRC_URI:stm32mpcommon = "${DIGI_PKG_SRC}/${BP}-${TF_FSCRYPT_ARCH}-libteec-v1.0.0.tar.gz;name=${TARGET_ARCH}-libteecv1" -SRC_URI[aarch64-libteecv1.md5sum] = "538f3bff6c9cd6110b7207581e4300d3" -SRC_URI[aarch64-libteecv1.sha256sum] = "a15a657f7350f9f5c9d39eb0f5d634174c1bc22d7e50333cc01de295e95c0fa4" +SRC_URI[aarch64-libteecv1.md5sum] = "67c2d87312ccae750de8165e82691c63" +SRC_URI[aarch64-libteecv1.sha256sum] = "43c2e900ca8d0aaac15963ffb5a7c57e3dd07613b2fe236955012039027b03f4" # Needed to resolve dependencies to libteec RDEPENDS:${PN} += "optee-client" From 3cf120227f324f549ced879dfdf496fb54da4348 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Thu, 24 Apr 2025 17:04:47 +0200 Subject: [PATCH 056/138] README: include ConnectCore 8M platforms Signed-off-by: Arturo Buzarra --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 7b183ad58..e6c5a400b 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,24 @@ Software for the following hardware platforms is in production support: * ConnectCore 93 Development Kit (DVK) * [CC-WMX93-KIT](https://www.digi.com/products/models/cc-wmx93-kit) ([Get Started](https://www.digi.com/resources/documentation/digidocs/embedded/dey/5.0/cc93/yocto-gs_index)) +## ConnectCore 8M Mini +* ConnectCore 8M Mini System-on-Module (SOM) + * [CC-WMX-ET8D-NN](https://www.digi.com/products/models/cc-wmx-et8d-nn) + * [CC-WMX-ET7D-NN](https://www.digi.com/products/models/cc-wmx-et7d-nn) + * [CC-MX-ET8D-ZN](https://www.digi.com/products/models/cc-mx-et8d-zn) + * [CC-MX-ET7D-ZN](https://www.digi.com/products/models/cc-mx-et7d-zn) +* ConnectCore 8M Mini Development Kit (DVK) + * [CC-WMX8MM-KIT](https://www.digi.com/products/models/cc-wmx8mm-kit) ([Get Started](https://www.digi.com/resources/documentation/digidocs/embedded/dey/5.0/cc8mmini/yocto-gs_index)) + +## ConnectCore 8M Nano +* ConnectCore 8M Nano System-on-Module (SOM) + * [CC-WMX-FS7D-NN](https://www.digi.com/products/models/cc-wmx-fs7d-nn) + * [CC-WMX-FR6D-NN](https://www.digi.com/products/models/cc-wmx-fr6d-nn) + * [CC-MX-FS7D-ZN](https://www.digi.com/products/models/cc-mx-fs7d-zn) + * [CC-MX-FR6D-ZN](https://www.digi.com/products/models/cc-mx-fr6d-zn) +* ConnectCore 8M Nano Development Kit (DVK) + * [CC-WMX8MN-KIT](https://www.digi.com/products/models/cc-wmx8mn-kit) ([Get Started](https://www.digi.com/resources/documentation/digidocs/embedded/dey/5.0/cc8mnano/yocto-gs_index)) + ## ConnectCore 8X * ConnectCore 8X System-on-Module (SOM) * [CC-WMX-JM8E-NN](https://www.digi.com/products/models/cc-wmx-jm8e-nn) From 6a1c8c27a981dc3f31c8ab5605eff053d7e87e59 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Mon, 28 Apr 2025 11:00:21 +0200 Subject: [PATCH 057/138] machine: ccimx8/9, ccmp1/2: configure default DTB in FIT image Configure the FIT_CONF_DEFAULT_DTB in the machine settings to define the default configuration for the generated FIT image. https://onedigi.atlassian.net/browse/DEL-9595 Signed-off-by: Javier Viguera --- meta-digi-arm/conf/machine/ccimx8mm-dvk.conf | 1 + meta-digi-arm/conf/machine/ccimx8mn-dvk.conf | 1 + meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf | 1 + meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf | 1 + meta-digi-arm/conf/machine/ccimx91-dvk.conf | 1 + meta-digi-arm/conf/machine/ccimx93-dvk.conf | 1 + meta-digi-arm/conf/machine/ccmp13-dvk.conf | 1 + meta-digi-arm/conf/machine/ccmp15-dvk.conf | 1 + meta-digi-arm/conf/machine/ccmp25-dvk.conf | 1 + 9 files changed, 9 insertions(+) diff --git a/meta-digi-arm/conf/machine/ccimx8mm-dvk.conf b/meta-digi-arm/conf/machine/ccimx8mm-dvk.conf index 394ead08c..ca47077fb 100644 --- a/meta-digi-arm/conf/machine/ccimx8mm-dvk.conf +++ b/meta-digi-arm/conf/machine/ccimx8mm-dvk.conf @@ -37,6 +37,7 @@ KERNEL_DEVICETREE ?= " \ digi/ccimx8m-dvk_user-leds.dtbo \ digi/ccimx8mm-dvk.dtb \ " +FIT_CONF_DEFAULT_DTB = "ccimx8mm-dvk.dtb" SERIAL_CONSOLES ?= "115200;ttymxc0" diff --git a/meta-digi-arm/conf/machine/ccimx8mn-dvk.conf b/meta-digi-arm/conf/machine/ccimx8mn-dvk.conf index ac27c3197..ffcd958bc 100644 --- a/meta-digi-arm/conf/machine/ccimx8mn-dvk.conf +++ b/meta-digi-arm/conf/machine/ccimx8mn-dvk.conf @@ -38,6 +38,7 @@ KERNEL_DEVICETREE ?= " \ digi/ccimx8mn-dvk_v1-v2.dtbo \ digi/ccimx8mn-dvk.dtb \ " +FIT_CONF_DEFAULT_DTB = "ccimx8mn-dvk.dtb" SERIAL_CONSOLES ?= "115200;ttymxc0" diff --git a/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf b/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf index 40a6a02f5..58a823e02 100644 --- a/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf +++ b/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf @@ -16,6 +16,7 @@ KERNEL_DEVICETREE ?= " \ digi/ccimx8x_wifi.dtbo \ digi/ccimx8x-sbc-express.dtb \ " +FIT_CONF_DEFAULT_DTB = "ccimx8x-sbc-express.dtb" SERIAL_CONSOLES ?= "115200;ttyLP2" diff --git a/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf b/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf index 679d9ab0d..778992419 100644 --- a/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf +++ b/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf @@ -31,6 +31,7 @@ KERNEL_DEVICETREE ?= " \ digi/ccimx8x-sbc-pro_v1-v3.dtbo \ digi/ccimx8x-sbc-pro.dtb \ " +FIT_CONF_DEFAULT_DTB = "ccimx8x-sbc-pro.dtb" SERIAL_CONSOLES ?= "115200;ttyLP2" diff --git a/meta-digi-arm/conf/machine/ccimx91-dvk.conf b/meta-digi-arm/conf/machine/ccimx91-dvk.conf index aa78f0b22..1a7f6ed4e 100644 --- a/meta-digi-arm/conf/machine/ccimx91-dvk.conf +++ b/meta-digi-arm/conf/machine/ccimx91-dvk.conf @@ -32,6 +32,7 @@ KERNEL_DEVICETREE ?= " \ digi/ccimx9_wifi.dtbo \ ${@bb.utils.contains('DISTRO_FEATURES', 'tsn', 'digi/ccimx9-dvk_eqos-tsn.dtbo', '', d)} \ " +FIT_CONF_DEFAULT_DTB = "ccimx91-dvk.dtb" # Default image for install scripts DEFAULT_IMAGE_NAME ?= "core-image-base" diff --git a/meta-digi-arm/conf/machine/ccimx93-dvk.conf b/meta-digi-arm/conf/machine/ccimx93-dvk.conf index 1f64f7144..b30ccfc2d 100644 --- a/meta-digi-arm/conf/machine/ccimx93-dvk.conf +++ b/meta-digi-arm/conf/machine/ccimx93-dvk.conf @@ -35,6 +35,7 @@ KERNEL_DEVICETREE ?= " \ digi/ccimx93-dvk.dtb \ ${@bb.utils.contains('DISTRO_FEATURES', 'tsn', 'digi/ccimx9-dvk_eqos-tsn.dtbo', '', d)} \ " +FIT_CONF_DEFAULT_DTB = "ccimx93-dvk.dtb" # Add additional firmware MACHINE_FIRMWARE:append = " ethos-u-firmware" diff --git a/meta-digi-arm/conf/machine/ccmp13-dvk.conf b/meta-digi-arm/conf/machine/ccmp13-dvk.conf index e6f114fec..a75aece69 100644 --- a/meta-digi-arm/conf/machine/ccmp13-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp13-dvk.conf @@ -45,6 +45,7 @@ STM32MP_KERNEL_DEVICETREE:ccmp13-dvk += " \ ccmp13-dvk_v2.dtbo \ ccmp133-dvk.dtb \ " +FIT_CONF_DEFAULT_DTB = "ccmp133-dvk.dtb" # Set DTB load address to U-Boot fdt_addr_r UBOOT_DTB_LOADADDRESS = "0xc4000000" # List of U-Boot device tree to use diff --git a/meta-digi-arm/conf/machine/ccmp15-dvk.conf b/meta-digi-arm/conf/machine/ccmp15-dvk.conf index 10f8fd799..daf3a748b 100644 --- a/meta-digi-arm/conf/machine/ccmp15-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp15-dvk.conf @@ -55,6 +55,7 @@ STM32MP_KERNEL_DEVICETREE:ccmp15-dvk += " \ ccmp15-dvk_v2.dtbo \ ccmp157-dvk.dtb \ " +FIT_CONF_DEFAULT_DTB = "ccmp157-dvk.dtb" # Set DTB load address to U-Boot fdt_addr_r UBOOT_DTB_LOADADDRESS = "0xc4000000" # List of U-Boot device tree to use diff --git a/meta-digi-arm/conf/machine/ccmp25-dvk.conf b/meta-digi-arm/conf/machine/ccmp25-dvk.conf index 728e5f9e8..b58f73999 100644 --- a/meta-digi-arm/conf/machine/ccmp25-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp25-dvk.conf @@ -59,6 +59,7 @@ STM32MP_KERNEL_DEVICETREE:ccmp25-dvk += " \ ccmp25-dvk_nhd-3-5-640480ef-msxp-mipi-dsi.dtbo \ ccmp25-dvk_usb-3-0-typec.dtbo \ " +FIT_CONF_DEFAULT_DTB = "ccmp25-dvk.dtb" # ========================================================================= # optee From 4da4eb7bddcab43e8f9aaa86573791660930f16c Mon Sep 17 00:00:00 2001 From: Isaac Hermida Date: Fri, 25 Apr 2025 15:42:35 +0200 Subject: [PATCH 058/138] udev-extraconf: skip actions for additional wifi interfaces When a WiFi card adds additional interfaces (for AP or WiFi direct), it generates udev triggers that lead the wifi card to fail. Ignore those events. The reason of this new behavior is due to poky commit 1e8b49999ad8. https://onedigi.atlassian.net/browse/DEL-9600 Signed-off-by: Isaac Hermida --- .../recipes-core/udev/udev-extraconf_1.1.bbappend | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/meta-digi-arm/recipes-core/udev/udev-extraconf_1.1.bbappend b/meta-digi-arm/recipes-core/udev/udev-extraconf_1.1.bbappend index 58f5fe13b..32f879806 100644 --- a/meta-digi-arm/recipes-core/udev/udev-extraconf_1.1.bbappend +++ b/meta-digi-arm/recipes-core/udev/udev-extraconf_1.1.bbappend @@ -11,14 +11,10 @@ SRC_URI:append:ccmp1 = " \ file://99-ext-rtc-wakeup.rules \ " -do_install:append:ccimx9() { - # The IW61x, when enabled as AP or P2P (or bridge with AP), generates udev triggers that lead the wifi card to fail. - # The text to replace is 'echo "$INTERFACE" | grep -q wifi && exit 0'. - sed -i -e 's|grep -q wifi|grep -q "uap0\|wfd0\|br0"|g' ${D}${sysconfdir}/udev/scripts/network.sh -} +WLAN_AP_INTERFACE = "wlan1" +WLAN_AP_INTERFACE:ccimx9 = "uap0" do_install:append() { - if [ -n "${@bb.utils.contains('IMAGE_FEATURES', 'read-only-rootfs', '1', '', d)}" ]; then install -d ${D}/mnt install -d ${D}/mnt/linux @@ -61,6 +57,10 @@ do_install:append() { # Fix mount.sh to force to find files in /tmp as symlink sed -i -e 's|find /tmp|find -L /tmp|g' ${D}${sysconfdir}/udev/scripts/mount.sh + + # When a WiFi driver adds AP or P2P (or bridge with AP), it generates udev triggers that lead the wifi card to fail. + # The grep argument is the list of ifaces to skip, in this case the AP, P2P, and bridge interfaces. + sed -i -e 's|grep -q wifi|grep -qE "${WLAN_AP_INTERFACE}\|${WLAN_P2P_INTERFACE}\|br0"|g' ${D}${sysconfdir}/udev/scripts/network.sh } do_install:append:ccmp1() { From 9b3117aa34068e1a6c90582f19877163aeadc1f6 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Fri, 28 Mar 2025 12:08:35 +0100 Subject: [PATCH 059/138] ccmp1: move ccmp1 platform support to kernel 6.6 and U-Boot v2023.10 Signed-off-by: Hector Palacios https://onedigi.atlassian.net/browse/DEL-9045 --- .../conf/machine/include/digi-defaults.inc | 4 +-- .../recipes-bsp/u-boot/u-boot-dey_2021.10.bb | 35 ------------------- .../recipes-bsp/u-boot/u-boot-dey_2023.10.bb | 2 +- .../recipes-kernel/linux/linux-dey_5.15.bb | 2 +- .../recipes-kernel/linux/linux-dey_6.6.bb | 12 +++---- 5 files changed, 10 insertions(+), 45 deletions(-) delete mode 100644 meta-digi-arm/recipes-bsp/u-boot/u-boot-dey_2021.10.bb diff --git a/meta-digi-arm/conf/machine/include/digi-defaults.inc b/meta-digi-arm/conf/machine/include/digi-defaults.inc index 3085862e5..3d33a0032 100644 --- a/meta-digi-arm/conf/machine/include/digi-defaults.inc +++ b/meta-digi-arm/conf/machine/include/digi-defaults.inc @@ -18,8 +18,8 @@ PREFERRED_PROVIDER_virtual/xserver = "xserver-xorg" # ccimx8mm 6.6 2024.04 (packaged in imx-boot) # ccimx91 6.6 2024.04 (packaged in imx-boot) # ccimx93 6.6 2024.04 (packaged in imx-boot) -# ccmp15 5.15 2021.10 -# ccmp13 5.15 2021.10 +# ccmp15 6.6 2023.10 +# ccmp13 6.6 2023.10 # ccmp25 6.6 2023.10 # diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey_2021.10.bb b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey_2021.10.bb deleted file mode 100644 index c0c8c011e..000000000 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey_2021.10.bb +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (C) 2022-2024, Digi International Inc. - -require u-boot-dey.inc -LIC_FILES_CHKSUM = "file://Licenses/README;md5=5a7450c57ffe5ae63fd732446b988025" - -DEPENDS += "flex-native bison-native" -DEPENDS += "python3-setuptools-native" - -SRCBRANCH = "v2021.10/master" -SRCREV = "${AUTOREV}" - -SRC_URI += " \ - ${@oe.utils.conditional('TRUSTFENCE_SIGN_FIT_STM', '1', 'file://fit_signature.cfg', '', d)} \ -" - -install_helper_files() { - # Install dtbs from UBOOT_DEVICETREE to datadir, so that kernel - # can use it for signing, and kernel will deploy after signs it. - if [ -n "${UBOOT_DEVICETREE}" ]; then - for devicetree in ${UBOOT_DEVICETREE}; do - install -Dm 0644 ${B}/${config}/arch/arm/dts/${devicetree}.dtb ${D}${datadir}/${devicetree}.dtb - done - else - bbwarn "${UBOOT_DEVICETREE} not found" - fi -} - -do_install:append() { - # Copy additional files, so kernel can use it when creating the FIT image - if [ "${KERNEL_IMAGETYPE}" = "fitImage" ]; then - install_helper_files - fi -} - -COMPATIBLE_MACHINE = "(ccmp1)" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey_2023.10.bb b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey_2023.10.bb index c31bd93a4..2a25a9d10 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey_2023.10.bb +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey_2023.10.bb @@ -32,4 +32,4 @@ do_install:append() { fi } -COMPATIBLE_MACHINE = "(ccmp2)" +COMPATIBLE_MACHINE = "(ccmp2|ccmp1)" diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey_5.15.bb b/meta-digi-arm/recipes-kernel/linux/linux-dey_5.15.bb index 23710a6c8..2902ed23c 100644 --- a/meta-digi-arm/recipes-kernel/linux/linux-dey_5.15.bb +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey_5.15.bb @@ -66,4 +66,4 @@ kernel_do_deploy:append:ccmp1() { fi } -COMPATIBLE_MACHINE = "(ccimx6$|ccmp1)" +COMPATIBLE_MACHINE = "(ccimx6$)" 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 fbd272215..99764725b 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 @@ -3,9 +3,9 @@ require recipes-kernel/linux/linux-dey.inc SRCBRANCH = "v6.6.52/nxp/master" -SRCBRANCH:stm32mp2common = "v6.6.48/stm/master" +SRCBRANCH:stm32mpcommon = "v6.6.48/stm/master" SRCREV = "${AUTOREV}" -SRCREV:stm32mp2common = "${AUTOREV}" +SRCREV:stm32mpcommon = "${AUTOREV}" # Define RT patches per machine RT_FILES:use-nxp-bsp = " \ @@ -38,7 +38,7 @@ module_conf_btnxpuart:ccimx9 = "blacklist btnxpuart" # --------------------------------------------------------------------- # stub for devicetree which are located on digi directory -do_compile:append:ccmp2() { +do_compile:append:stm32mpcommon() { if [ -d "${B}/arch/${ARCH}/boot/dts/digi" ]; then for dtbf in ${KERNEL_DEVICETREE}; do install -m 0644 "${B}/arch/${ARCH}/boot/dts/digi/${dtbf}" "${B}/arch/${ARCH}/boot/dts/" @@ -46,7 +46,7 @@ do_compile:append:ccmp2() { fi } -do_install:append:ccmp2() { +do_install:append:stm32mpcommon() { if ${@bb.utils.contains('MACHINE_FEATURES','gpu','true','false',d)}; then # when ACCEPT_EULA are filled install -d ${D}/${sysconfdir}/modprobe.d/ @@ -54,6 +54,6 @@ do_install:append:ccmp2() { fi } -FILES:${KERNEL_PACKAGE_NAME}-modules:ccmp2 += "${sysconfdir}/modprobe.d" +FILES:${KERNEL_PACKAGE_NAME}-modules:stm32mpcommon += "${sysconfdir}/modprobe.d" -COMPATIBLE_MACHINE = "(ccimx6ul|ccimx8m|ccimx8x|ccimx9|ccmp2)" +COMPATIBLE_MACHINE = "(ccimx6ul|ccimx8m|ccimx8x|ccimx9|ccmp2|ccmp1)" From 5d640e5c2a56f35902cc22059bb145c3862343b8 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Fri, 28 Mar 2025 12:08:55 +0100 Subject: [PATCH 060/138] README: add ccmp1 to the list of supported platforms Signed-off-by: Hector Palacios https://onedigi.atlassian.net/browse/DEL-9045 --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index e6c5a400b..cf0406762 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,20 @@ Software for the following hardware platforms is in production support: * ConnectCore 93 Development Kit (DVK) * [CC-WMX93-KIT](https://www.digi.com/products/models/cc-wmx93-kit) ([Get Started](https://www.digi.com/resources/documentation/digidocs/embedded/dey/5.0/cc93/yocto-gs_index)) +## ConnectCore MP13 +* ConnectCore MP13 System-on-Module (SOM) + * [CC-WST-DX58-NK](https://www.digi.com/products/models/cc-wst-dx58-nk) + * [CC-ST-DX58-ZK](https://www.digi.com/products/models/cc-st-dx58-zk) +* ConnectCore MP13 Development Kit (DVK) + * [CC-WMP133-KIT](https://www.digi.com/products/models/cc-wmp133-kit) ([Get Started](https://www.digi.com/resources/documentation/digidocs/embedded/dey/4.0/ccmp13/yocto-gs_index)) + +## ConnectCore MP15 +* ConnectCore MP15 System-on-Module (SOM) + * [CC-WST-DW69-NM](https://www.digi.com/products/models/cc-wst-dw69-nm) + * [CC-ST-DW69-ZM](https://www.digi.com/products/models/cc-st-dw69-zm) +* ConnectCore MP15 Development Kit (DVK) + * [CC-WMP157-KIT](https://www.digi.com/products/models/cc-wmp157-kit) ([Get Started](https://www.digi.com/resources/documentation/digidocs/embedded/dey/4.0/ccmp15/yocto-gs_index)) + ## ConnectCore 8M Mini * ConnectCore 8M Mini System-on-Module (SOM) * [CC-WMX-ET8D-NN](https://www.digi.com/products/models/cc-wmx-et8d-nn) From b4c5345be8b3a727851a5e9541b78bf22f95ea7e Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Fri, 28 Mar 2025 12:36:28 +0100 Subject: [PATCH 061/138] ccmp1: remove 'kernel-headers' dependency This commit removes kernel headers from the SDK package list to prevent overwriting the default ones, as there are currently no ST-specific headers required. Reference: ebadb27d60bb21ba027f435440a7e95f4d69649d Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/include/ccmp1.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/meta-digi-arm/conf/machine/include/ccmp1.inc b/meta-digi-arm/conf/machine/include/ccmp1.inc index 9d2488217..60082d3d7 100644 --- a/meta-digi-arm/conf/machine/include/ccmp1.inc +++ b/meta-digi-arm/conf/machine/include/ccmp1.inc @@ -83,3 +83,6 @@ UBOOT_ENV = "boot" # Partitions to blacklist for swupdate: # fsbl1, fsbl2, metadata1, metadata2, fip-a, fip-b SWUPDATE_MTD_BLACKLIST = "0 1 2 3 4 5" + +# Remove custom kernel headers from toolchain +ST_TOOLS_FOR_TARGET_SDK:remove = "kernel-headers" From d21c7b5fdd1a91d8b368069fa84a23042afda148 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Thu, 10 Apr 2025 10:54:07 +0200 Subject: [PATCH 062/138] ccmp1: update BOOTDEVICE_LABELS string for nand to new BSP Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/include/ccmp1.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meta-digi-arm/conf/machine/include/ccmp1.inc b/meta-digi-arm/conf/machine/include/ccmp1.inc index 60082d3d7..0d8b41b3e 100644 --- a/meta-digi-arm/conf/machine/include/ccmp1.inc +++ b/meta-digi-arm/conf/machine/include/ccmp1.inc @@ -58,13 +58,13 @@ IMAGE_FSTYPES:remove = "ext4" IMAGE_FSTYPES:append = " ${@bb.utils.contains('BOOTDEVICE_LABELS', 'sdcard', 'boot.vfat ext4.gz', '', d)}" # List of supported boot devices -BOOTDEVICE_LABELS ?= "nand-4-256" +BOOTDEVICE_LABELS ?= "nand-4-256-1024 usb" # Supported boot schemes BOOTSCHEME_LABELS ?= "optee" # Remove NAND-based ubi/ubifs images added by st-machine-common-stm32mp.inc # because image_types_digi.bbclass already builds their own. -MULTIUBI_BUILD:remove = "nand_4_256" +MULTIUBI_BUILD:remove = "nand-4-256-1024" IMAGE_FSTYPES:remove = "stmultiubi" # Wic files From 4bdd4b0b56f862207022fdf5fc09a6183379ed3e Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Thu, 10 Apr 2025 11:02:43 +0200 Subject: [PATCH 063/138] ccmp1: define device trees per media STM32MP_DT_FILES_xxx The variable STM32MP_DEVICETREE is later expanded with the list of values in all media. Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/ccmp13-dvk.conf | 7 +++++-- meta-digi-arm/conf/machine/ccmp15-dvk.conf | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/meta-digi-arm/conf/machine/ccmp13-dvk.conf b/meta-digi-arm/conf/machine/ccmp13-dvk.conf index a75aece69..6cbcbbb32 100644 --- a/meta-digi-arm/conf/machine/ccmp13-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp13-dvk.conf @@ -31,8 +31,11 @@ UBOOT_CONFIG[ccmp13-dvk] = "ccmp13-dvk_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" # ========================================================================= # Machine settings # ========================================================================= -# Define list of devicetree per board -STM32MP_DEVICETREE ??= "ccmp13-dvk-256MB" +# Define list of devicetree per supported storage +STM32MP_DT_FILES_NAND = "ccmp13-dvk-256MB" +STM32MP_DT_FILES_SDCARD = "${STM32MP_DT_FILES_NAND}" +STM32MP_DT_FILES_USB = "${STM32MP_DT_FILES_NAND}" + # Extra DTB for board - need to specify it with .dtb ... STM32MP_KERNEL_DEVICETREE:ccmp13-dvk += " \ ccmp13_bt_test.dtbo \ diff --git a/meta-digi-arm/conf/machine/ccmp15-dvk.conf b/meta-digi-arm/conf/machine/ccmp15-dvk.conf index daf3a748b..351aad17f 100644 --- a/meta-digi-arm/conf/machine/ccmp15-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp15-dvk.conf @@ -31,8 +31,11 @@ UBOOT_CONFIG[ccmp15-dvk] = "ccmp15-dvk_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" # ========================================================================= # Machine settings # ========================================================================= -# Define list of devicetree per board -STM32MP_DEVICETREE ??= "ccmp15-dvk-512MB ccmp15-dvk-1GB" +# Define list of devicetree per supported storage +STM32MP_DT_FILES_NAND = "ccmp15-dvk-512MB ccmp15-dvk-1GB" +STM32MP_DT_FILES_SDCARD = "${STM32MP_DT_FILES_NAND}" +STM32MP_DT_FILES_USB = "${STM32MP_DT_FILES_NAND}" + # Extra DTB for board - need to specify it with .dtb ... STM32MP_KERNEL_DEVICETREE:ccmp15-dvk += " \ ccmp15_bt_test.dtbo \ From 7ceebfbb1e24d06bf1914e216a2f2550b7bba32f Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Thu, 10 Apr 2025 13:55:56 +0200 Subject: [PATCH 064/138] ccmp1: adapt U-Boot and OPTEE variables to newest ST BSP Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/ccmp13-dvk.conf | 4 ++-- meta-digi-arm/conf/machine/ccmp15-dvk.conf | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/meta-digi-arm/conf/machine/ccmp13-dvk.conf b/meta-digi-arm/conf/machine/ccmp13-dvk.conf index 6cbcbbb32..54fce1d0e 100644 --- a/meta-digi-arm/conf/machine/ccmp13-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp13-dvk.conf @@ -26,7 +26,7 @@ DEVICE_BOARD_ENABLE:SDCARD += "${@bb.utils.contains('BOOTDEVICE_LABELS', 'sdcard # ========================================================================= # Last one is the default (the one the symlinks point at) UBOOT_CONFIG = "ccmp13-dvk" -UBOOT_CONFIG[ccmp13-dvk] = "ccmp13-dvk_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" +UBOOT_CONFIG[ccmp13-dvk] = "ccmp13-dvk_defconfig,,u-boot.dtb" # ========================================================================= # Machine settings @@ -108,7 +108,7 @@ ST_DEBUG_TRACE = "0" # optee # ========================================================================= # Map OPTEE configuration to device tree list -OPTEE_CONF = "${STM32MP_DEVICETREE}" +OPTEE_DEVICETREE_optee = "${STM32MP_DEVICETREE}" # ========================================================================= # Flashlayouts diff --git a/meta-digi-arm/conf/machine/ccmp15-dvk.conf b/meta-digi-arm/conf/machine/ccmp15-dvk.conf index 351aad17f..aa89aab05 100644 --- a/meta-digi-arm/conf/machine/ccmp15-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp15-dvk.conf @@ -26,7 +26,7 @@ DEVICE_BOARD_ENABLE:SDCARD += "${@bb.utils.contains('BOOTDEVICE_LABELS', 'sdcard # ========================================================================= # Last one is the default (the one the symlinks point at) UBOOT_CONFIG = "ccmp15-dvk" -UBOOT_CONFIG[ccmp15-dvk] = "ccmp15-dvk_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" +UBOOT_CONFIG[ccmp15-dvk] = "ccmp15-dvk_defconfig,,u-boot.dtb" # ========================================================================= # Machine settings @@ -122,7 +122,7 @@ ST_DEBUG_TRACE = "0" # optee # ========================================================================= # Map OPTEE configuration to device tree list -OPTEE_CONF = "${STM32MP_DEVICETREE}" +OPTEE_DEVICETREE_optee = "${STM32MP_DEVICETREE}" # ========================================================================= # Flashlayouts From 7722e3a1d2f9a11a4506416f0f28a11cb6628860 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Fri, 11 Apr 2025 11:40:05 +0200 Subject: [PATCH 065/138] ccmp1: [cosmetic] re-order definitions to match the ccmp25 layout Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/ccmp13-dvk.conf | 16 +++++----- meta-digi-arm/conf/machine/ccmp15-dvk.conf | 16 +++++----- meta-digi-arm/conf/machine/include/ccmp1.inc | 32 ++++++++++---------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/meta-digi-arm/conf/machine/ccmp13-dvk.conf b/meta-digi-arm/conf/machine/ccmp13-dvk.conf index 54fce1d0e..6476ce159 100644 --- a/meta-digi-arm/conf/machine/ccmp13-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp13-dvk.conf @@ -27,6 +27,8 @@ DEVICE_BOARD_ENABLE:SDCARD += "${@bb.utils.contains('BOOTDEVICE_LABELS', 'sdcard # Last one is the default (the one the symlinks point at) UBOOT_CONFIG = "ccmp13-dvk" UBOOT_CONFIG[ccmp13-dvk] = "ccmp13-dvk_defconfig,,u-boot.dtb" +# List of U-Boot device tree to use +UBOOT_DEVICETREE = "${STM32MP_DEVICETREE}" # ========================================================================= # Machine settings @@ -51,8 +53,12 @@ STM32MP_KERNEL_DEVICETREE:ccmp13-dvk += " \ FIT_CONF_DEFAULT_DTB = "ccmp133-dvk.dtb" # Set DTB load address to U-Boot fdt_addr_r UBOOT_DTB_LOADADDRESS = "0xc4000000" -# List of U-Boot device tree to use -UBOOT_DEVICETREE = "${STM32MP_DEVICETREE}" + +# ========================================================================= +# optee +# ========================================================================= +# Map OPTEE configuration to device tree list +OPTEE_DEVICETREE_optee = "${STM32MP_DEVICETREE}" # ========================================================================= # Machine features @@ -104,12 +110,6 @@ ST_TOOLS_FOR_SDK:remove = "nativesdk-gcc-arm-none-eabi" # activate/deactivate the debug and trace on boot stage ST_DEBUG_TRACE = "0" -# ========================================================================= -# optee -# ========================================================================= -# Map OPTEE configuration to device tree list -OPTEE_DEVICETREE_optee = "${STM32MP_DEVICETREE}" - # ========================================================================= # Flashlayouts # ========================================================================= diff --git a/meta-digi-arm/conf/machine/ccmp15-dvk.conf b/meta-digi-arm/conf/machine/ccmp15-dvk.conf index aa89aab05..bf04babd2 100644 --- a/meta-digi-arm/conf/machine/ccmp15-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp15-dvk.conf @@ -27,6 +27,8 @@ DEVICE_BOARD_ENABLE:SDCARD += "${@bb.utils.contains('BOOTDEVICE_LABELS', 'sdcard # Last one is the default (the one the symlinks point at) UBOOT_CONFIG = "ccmp15-dvk" UBOOT_CONFIG[ccmp15-dvk] = "ccmp15-dvk_defconfig,,u-boot.dtb" +# List of U-Boot device tree to use +UBOOT_DEVICETREE = "${STM32MP_DEVICETREE}" # ========================================================================= # Machine settings @@ -59,10 +61,14 @@ STM32MP_KERNEL_DEVICETREE:ccmp15-dvk += " \ ccmp157-dvk.dtb \ " FIT_CONF_DEFAULT_DTB = "ccmp157-dvk.dtb" + +# ========================================================================= +# optee +# ========================================================================= +# Map OPTEE configuration to device tree list +OPTEE_DEVICETREE_optee = "${STM32MP_DEVICETREE}" # Set DTB load address to U-Boot fdt_addr_r UBOOT_DTB_LOADADDRESS = "0xc4000000" -# List of U-Boot device tree to use -UBOOT_DEVICETREE = "${STM32MP_DEVICETREE}" # ========================================================================= # Machine features @@ -118,12 +124,6 @@ ST_TOOLS_FOR_SDK:remove = "nativesdk-gcc-arm-none-eabi" # activate/deactivate the debug and trace on boot stage ST_DEBUG_TRACE = "0" -# ========================================================================= -# optee -# ========================================================================= -# Map OPTEE configuration to device tree list -OPTEE_DEVICETREE_optee = "${STM32MP_DEVICETREE}" - # ========================================================================= # Flashlayouts # ========================================================================= diff --git a/meta-digi-arm/conf/machine/include/ccmp1.inc b/meta-digi-arm/conf/machine/include/ccmp1.inc index 0d8b41b3e..f17a6531d 100644 --- a/meta-digi-arm/conf/machine/include/ccmp1.inc +++ b/meta-digi-arm/conf/machine/include/ccmp1.inc @@ -11,11 +11,27 @@ DIGI_FAMILY = "ccmp1" # Platform T-FA settings TFA_SIGN_SUFFIX ?= "${@bb.utils.contains('TRUSTFENCE_SIGN', '1', '_signed', '', d)}" +# ========================================================================= +# U-Boot +# ========================================================================= # Platform u-boot settings UBOOT_PREFIX = "fip" UBOOT_SUFFIX = "bin" # Set U-Boot FIP settings FIP_UBOOT_DTB = "u-boot" +# Include boot script into the FIT image +UBOOT_ENV = "boot" +# Set InitRAMDisk load address to U-boot initrd_addr +UBOOT_RD_LOADADDRESS = "0xc4400000" +# U-Boot environment offset (within partition) +UBOOT_ENV_OFFSET ?= "0x0" +UBOOT_ENV_REDUND_OFFSET ?= "0x0" +# U-Boot environment size +UBOOT_ENV_SIZE ?= "0x20000" +# U-Boot environment range: size (in hex) in the environment partition that +# the U-Boot environment can take up (if undefined, it will take up all the +# available space in the environment partition) +UBOOT_ENV_RANGE ?= "" FIP_SIGN_SUFFIX ?= "${@bb.utils.contains('TRUSTFENCE_SIGN', '1', '_Signed', '', d)}" STORAGE_MEDIA = "mtd" @@ -32,19 +48,6 @@ ROOTFS_DEV_NAME ?= "rootfs" ROOTFS_ENC_DEV = "${ROOTFS_DEV_NAME}" ROOTFS_DEV_NAME_FINAL = "${@oe.utils.ifelse(d.getVar('TRUSTFENCE_ENCRYPT_ROOTFS') == '1', '${ROOTFS_ENC_DEV}', '${ROOTFS_DEV_NAME}')}" -# U-Boot environment offset (within partition) -UBOOT_ENV_OFFSET ?= "0x0" -UBOOT_ENV_REDUND_OFFSET ?= "0x0" -# U-Boot environment size -UBOOT_ENV_SIZE ?= "0x20000" -# U-Boot environment range: size (in hex) in the environment partition that -# the U-Boot environment can take up (if undefined, it will take up all the -# available space in the environment partition) -UBOOT_ENV_RANGE ?= "" - -# Set InitRAMDisk load address to U-boot initrd_addr -UBOOT_RD_LOADADDRESS = "0xc4400000" - # OPTEE runtime packages to install OPTEE_PKGS ??= "optee-client" @@ -77,9 +80,6 @@ WKS_FILE_DEPENDS ?= " \ # Disable the generation of flashlayout files ENABLE_FLASHLAYOUT_CONFIG = "0" -# Include boot script into the FIT image -UBOOT_ENV = "boot" - # Partitions to blacklist for swupdate: # fsbl1, fsbl2, metadata1, metadata2, fip-a, fip-b SWUPDATE_MTD_BLACKLIST = "0 1 2 3 4 5" From 4d37569a522971df58a1a1d2f589a424a4d209d1 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Fri, 11 Apr 2025 11:53:20 +0200 Subject: [PATCH 066/138] ccmp1: move common ccmp15/ccmp13 defines to common include file Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/ccmp13-dvk.conf | 10 ---------- meta-digi-arm/conf/machine/ccmp15-dvk.conf | 11 +---------- meta-digi-arm/conf/machine/include/ccmp1.inc | 12 ++++++++++++ 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/meta-digi-arm/conf/machine/ccmp13-dvk.conf b/meta-digi-arm/conf/machine/ccmp13-dvk.conf index 6476ce159..c514cc61d 100644 --- a/meta-digi-arm/conf/machine/ccmp13-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp13-dvk.conf @@ -51,8 +51,6 @@ STM32MP_KERNEL_DEVICETREE:ccmp13-dvk += " \ ccmp133-dvk.dtb \ " FIT_CONF_DEFAULT_DTB = "ccmp133-dvk.dtb" -# Set DTB load address to U-Boot fdt_addr_r -UBOOT_DTB_LOADADDRESS = "0xc4000000" # ========================================================================= # optee @@ -114,14 +112,6 @@ ST_DEBUG_TRACE = "0" # Flashlayouts # ========================================================================= -# ========================================================================= -# Kernel -# ========================================================================= -KERNEL_IMAGETYPE = "${@bb.utils.contains('TRUSTFENCE_SIGN_FIT_STM', '1', 'fitImage', 'zImage', d)}" -KERNEL_CLASSES = "kernel-fitimage" -KERNEL_ALT_IMAGETYPE = "Image vmlinux" -KERNEL_DEFCONFIG ?= "arch/arm/configs/ccmp1_defconfig" - # ========================================================================= # DEY settings # ========================================================================= diff --git a/meta-digi-arm/conf/machine/ccmp15-dvk.conf b/meta-digi-arm/conf/machine/ccmp15-dvk.conf index bf04babd2..8f4b85e83 100644 --- a/meta-digi-arm/conf/machine/ccmp15-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp15-dvk.conf @@ -67,8 +67,6 @@ FIT_CONF_DEFAULT_DTB = "ccmp157-dvk.dtb" # ========================================================================= # Map OPTEE configuration to device tree list OPTEE_DEVICETREE_optee = "${STM32MP_DEVICETREE}" -# Set DTB load address to U-Boot fdt_addr_r -UBOOT_DTB_LOADADDRESS = "0xc4000000" # ========================================================================= # Machine features @@ -128,14 +126,6 @@ ST_DEBUG_TRACE = "0" # Flashlayouts # ========================================================================= -# ========================================================================= -# Kernel -# ========================================================================= -KERNEL_IMAGETYPE = "${@bb.utils.contains('TRUSTFENCE_SIGN_FIT_STM', '1', 'fitImage', 'zImage', d)}" -KERNEL_CLASSES = "kernel-fitimage" -KERNEL_ALT_IMAGETYPE = "Image vmlinux" -KERNEL_DEFCONFIG ?= "arch/arm/configs/ccmp1_defconfig" - # ========================================================================= # DEY settings # ========================================================================= @@ -174,6 +164,7 @@ MACHINE_DISTRO_FEATURES_REMOVE = "vulkan x11" # Disable use of vendorfs partition ST_VENDORFS = "0" +ST_USERFS = "0" # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = " \ diff --git a/meta-digi-arm/conf/machine/include/ccmp1.inc b/meta-digi-arm/conf/machine/include/ccmp1.inc index f17a6531d..1fadb7934 100644 --- a/meta-digi-arm/conf/machine/include/ccmp1.inc +++ b/meta-digi-arm/conf/machine/include/ccmp1.inc @@ -21,6 +21,9 @@ UBOOT_SUFFIX = "bin" FIP_UBOOT_DTB = "u-boot" # Include boot script into the FIT image UBOOT_ENV = "boot" +# For FIT image usage +UBOOT_DTB_LOADADDRESS = "0xc4000000" +UBOOT_DTBO_LOADADDRESS = "0xc4000000" # Set InitRAMDisk load address to U-boot initrd_addr UBOOT_RD_LOADADDRESS = "0xc4400000" # U-Boot environment offset (within partition) @@ -32,8 +35,17 @@ UBOOT_ENV_SIZE ?= "0x20000" # the U-Boot environment can take up (if undefined, it will take up all the # available space in the environment partition) UBOOT_ENV_RANGE ?= "" + FIP_SIGN_SUFFIX ?= "${@bb.utils.contains('TRUSTFENCE_SIGN', '1', '_Signed', '', d)}" +# ========================================================================= +# Kernel +# ========================================================================= +KERNEL_IMAGETYPE = "${@bb.utils.contains('TRUSTFENCE_SIGN_FIT_STM', '1', 'fitImage', 'zImage', d)}" +KERNEL_CLASSES = "kernel-fitimage" +KERNEL_ALT_IMAGETYPE = "Image vmlinux" +KERNEL_DEFCONFIG ?= "arch/arm/configs/ccmp1_defconfig" + STORAGE_MEDIA = "mtd" # Enable STM specific features From 2a547ac302daa2f0a3232c4090e9e065a31a578b Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Fri, 11 Apr 2025 12:57:32 +0200 Subject: [PATCH 067/138] ccmp1: disable STM32MP_DEVICETREE_PROGRAMMER_ENABLE Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/include/ccmp1.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/meta-digi-arm/conf/machine/include/ccmp1.inc b/meta-digi-arm/conf/machine/include/ccmp1.inc index 1fadb7934..33229990f 100644 --- a/meta-digi-arm/conf/machine/include/ccmp1.inc +++ b/meta-digi-arm/conf/machine/include/ccmp1.inc @@ -60,6 +60,9 @@ ROOTFS_DEV_NAME ?= "rootfs" ROOTFS_ENC_DEV = "${ROOTFS_DEV_NAME}" ROOTFS_DEV_NAME_FINAL = "${@oe.utils.ifelse(d.getVar('TRUSTFENCE_ENCRYPT_ROOTFS') == '1', '${ROOTFS_ENC_DEV}', '${ROOTFS_DEV_NAME}')}" +# Disable by default programmer support: +STM32MP_DEVICETREE_PROGRAMMER_ENABLE ?= "0" + # OPTEE runtime packages to install OPTEE_PKGS ??= "optee-client" From 65a0954f07e080d4e8dfa4fae30f27a986bfe5d1 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Fri, 11 Apr 2025 12:58:58 +0200 Subject: [PATCH 068/138] u-boot-dey: make function to install artifacts in sysroot common to ccmp1/ccmp2 Signed-off-by: Hector Palacios --- meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc index 8bcb66272..40845582c 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc @@ -282,7 +282,7 @@ u_boot_sysroot_populate() { install -m 0777 ${B}/${config}/u-boot-nodtb.bin ${dest}/u-boot-nodtb-${devicetree}.bin done } -SYSROOT_PREPROCESS_FUNCS:ccmp2 =+ "u_boot_sysroot_populate" -SYSROOT_DIRS:append:ccmp2 = " ${FIP_DIR_UBOOT}" +SYSROOT_PREPROCESS_FUNCS:stm32mpcommon =+ "u_boot_sysroot_populate" +SYSROOT_DIRS:append:stm32mpcommon = " ${FIP_DIR_UBOOT}" SYSROOT_DIRS += "/boot" From 543cb5ceddc9f056ca06015908a2867aa4d7f1d7 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Fri, 11 Apr 2025 13:00:39 +0200 Subject: [PATCH 069/138] u-boot-dey: install all variants of U-Boot binaries The function had into account the memory variants when copying U-Boot device trees, but not the U-Boot binaries themselves. Signed-off-by: Hector Palacios --- meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc index 40845582c..4d0edc6c9 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc @@ -277,9 +277,10 @@ u_boot_sysroot_populate() { for devicetree in ${UBOOT_DEVICETREE}; do # Install u-boot dtb install -m 644 ${B}/${config}/arch/arm/dts/${devicetree}.dtb ${dest}/${FIP_UBOOT_DTB}-${devicetree}.dtb + # Install u-boot binary + install -m 0777 ${B}/${config}/u-boot-nodtb.bin ${dest}/u-boot-nodtb-${devicetree}.bin done fi - install -m 0777 ${B}/${config}/u-boot-nodtb.bin ${dest}/u-boot-nodtb-${devicetree}.bin done } SYSROOT_PREPROCESS_FUNCS:stm32mpcommon =+ "u_boot_sysroot_populate" From e2f1b3cb645db08deb982d0b658583ef00018c3b Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Mon, 14 Apr 2025 13:36:05 +0200 Subject: [PATCH 070/138] tf-a-stm32mp: fix TF_A_CONFIG for nand On the new BSP the configuration is called 'optee-nand' and the build parameters have changed. We do this override in meta-digi only to incorporate `STM32MP_USB_PROGRAMMER=1` parameter, which allows to boot the nand image from USB, too. Signed-off-by: Hector Palacios --- .../trusted-firmware-a/tf-a-stm32mp_2.10.bbappend | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend index 48ddd4170..4e952c2f4 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend @@ -24,7 +24,12 @@ SIGN_TOOL_EXTRA_soc:ccmp13 = " ${@bb.utils.contains('ENCRYPT_ENABLE', '1', '-of # stm32mp2 = header-version 2.2 SIGN_TOOL_EXTRA_soc:stm32mp2common = " --header-version 2.2 ${@bb.utils.contains('ENCRYPT_ENABLE', '1', '-of ${TF_A_ENCRYPT_OF}', '-of ${TF_A_SIGN_OF}', d)}" -TF_A_CONFIG[nand] = "${DEVICE_BOARD_ENABLE:NAND},STM32MP_RAW_NAND=1 ${@'STM32MP_FORCE_MTD_START_OFFSET=${TF_A_MTD_START_OFFSET_NAND}' if ${TF_A_MTD_START_OFFSET_NAND} else ''} STM32MP_USB_PROGRAMMER=1" +TF_A_CONFIG[optee-nand] = "\ + ${STM32MP_DT_FILES_NAND},\ + ${TF_A_CONFIG_OPTS_optee} ${TF_A_CONFIG_OPTS_features} ${TF_A_CONFIG_OPTS_fwupdate} STM32MP_RAW_NAND=1 STM32MP_USB_PROGRAMMER=1 ${@'STM32MP_FORCE_MTD_START_OFFSET=${TF_A_MTD_START_OFFSET_NAND}' if ${TF_A_MTD_START_OFFSET_NAND} else ''},\ + ${TF_A_CONFIG_BASENAME_BIN},\ + ${TF_A_CONFIG_MAKE_TARGET},\ + ${TF_A_CONFIG_DEPLOY_FTYPE} ${TF_A_CONFIG_DEPLOY_EXTRA}" # TF_A_CONFIG[uart] (same as 'optee-programmer-uart') TF_A_CONFIG[uart] ?= "\ ${STM32MP_DEVICETREE_PROGRAMMER},\ From e644965b0d16fc0bc69c4261f7fda6391f535554 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Mon, 14 Apr 2025 13:41:44 +0200 Subject: [PATCH 071/138] ccmp1x: add 'stm32mp15common'/'stm32mp13common' to MACHINEOVERRIDES This override sets other variables, such as STM32MP_SOC_NAME which is used on the build of the FIP image. It also makes ST layer append a new UBOOT_CONFIG (default_stm32mp15/13) that we must remove. Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/ccmp13-dvk.conf | 4 +++- meta-digi-arm/conf/machine/ccmp15-dvk.conf | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/meta-digi-arm/conf/machine/ccmp13-dvk.conf b/meta-digi-arm/conf/machine/ccmp13-dvk.conf index c514cc61d..f9db7e410 100644 --- a/meta-digi-arm/conf/machine/ccmp13-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp13-dvk.conf @@ -12,7 +12,7 @@ DIGI_SOM = "ccmp13" # Chip architecture # ========================================================================= DEFAULTTUNE = "cortexa7thf-neon-vfpv4" -MACHINEOVERRIDES = "arm:armv7ve:stcommon:stm32mpcommon:stm32mp1common:${DIGI_FAMILY}:${DIGI_SOM}:ccmp13-dvk" +MACHINEOVERRIDES = "arm:armv7ve:stcommon:stm32mpcommon:stm32mp1common:stm32mp13common:${DIGI_FAMILY}:${DIGI_SOM}:ccmp13-dvk" # ========================================================================= # boot device @@ -27,6 +27,8 @@ DEVICE_BOARD_ENABLE:SDCARD += "${@bb.utils.contains('BOOTDEVICE_LABELS', 'sdcard # Last one is the default (the one the symlinks point at) UBOOT_CONFIG = "ccmp13-dvk" UBOOT_CONFIG[ccmp13-dvk] = "ccmp13-dvk_defconfig,,u-boot.dtb" +# Remove STM32mp13 configs from ST layer +UBOOT_CONFIG:remove:stm32mp13common = "default_stm32mp13" # List of U-Boot device tree to use UBOOT_DEVICETREE = "${STM32MP_DEVICETREE}" diff --git a/meta-digi-arm/conf/machine/ccmp15-dvk.conf b/meta-digi-arm/conf/machine/ccmp15-dvk.conf index 8f4b85e83..330d3b52c 100644 --- a/meta-digi-arm/conf/machine/ccmp15-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp15-dvk.conf @@ -12,7 +12,7 @@ DIGI_SOM = "ccmp15" # Chip architecture # ========================================================================= DEFAULTTUNE = "cortexa7thf-neon-vfpv4" -MACHINEOVERRIDES = "arm:armv7ve:stcommon:stm32mpcommon:stm32mp1common:${DIGI_FAMILY}:${DIGI_SOM}:ccmp15-dvk" +MACHINEOVERRIDES = "arm:armv7ve:stcommon:stm32mpcommon:stm32mp1common:stm32mp15common:${DIGI_FAMILY}:${DIGI_SOM}:ccmp15-dvk" # ========================================================================= # boot device @@ -27,6 +27,8 @@ DEVICE_BOARD_ENABLE:SDCARD += "${@bb.utils.contains('BOOTDEVICE_LABELS', 'sdcard # Last one is the default (the one the symlinks point at) UBOOT_CONFIG = "ccmp15-dvk" UBOOT_CONFIG[ccmp15-dvk] = "ccmp15-dvk_defconfig,,u-boot.dtb" +# Remove STM32mp15 configs from ST layer +UBOOT_CONFIG:remove:stm32mp15common = "default_stm32mp15" # List of U-Boot device tree to use UBOOT_DEVICETREE = "${STM32MP_DEVICETREE}" From 51116d4065ee128c69f94cf8a58aaca8fdab7dce Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Wed, 23 Apr 2025 13:02:30 +0200 Subject: [PATCH 072/138] ccmp1: use 'opteemin' BOOTSCHEME_LABELS for ccmp15 by default Move BOOTSCHEME_LABELS from common include to platform config Default to: - 'opteemin' for ccmp15 - 'optee' for ccmp13 This doesn't change the behavior present in DEY-4.0, where OPTEE in ccmp15 was minimal and running on non-secure DDR. Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/ccmp13-dvk.conf | 3 +++ meta-digi-arm/conf/machine/ccmp15-dvk.conf | 3 +++ meta-digi-arm/conf/machine/include/ccmp1.inc | 2 -- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/meta-digi-arm/conf/machine/ccmp13-dvk.conf b/meta-digi-arm/conf/machine/ccmp13-dvk.conf index f9db7e410..86bf5054a 100644 --- a/meta-digi-arm/conf/machine/ccmp13-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp13-dvk.conf @@ -59,6 +59,9 @@ FIT_CONF_DEFAULT_DTB = "ccmp133-dvk.dtb" # ========================================================================= # Map OPTEE configuration to device tree list OPTEE_DEVICETREE_optee = "${STM32MP_DEVICETREE}" +OPTEE_DEVICETREE_opteemin = "${STM32MP_DEVICETREE}" +# Supported boot schemes +BOOTSCHEME_LABELS ?= "optee" # ========================================================================= # Machine features diff --git a/meta-digi-arm/conf/machine/ccmp15-dvk.conf b/meta-digi-arm/conf/machine/ccmp15-dvk.conf index 330d3b52c..89890f290 100644 --- a/meta-digi-arm/conf/machine/ccmp15-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp15-dvk.conf @@ -69,6 +69,9 @@ FIT_CONF_DEFAULT_DTB = "ccmp157-dvk.dtb" # ========================================================================= # Map OPTEE configuration to device tree list OPTEE_DEVICETREE_optee = "${STM32MP_DEVICETREE}" +OPTEE_DEVICETREE_opteemin = "${STM32MP_DEVICETREE}" +# Supported boot schemes +BOOTSCHEME_LABELS ?= "opteemin" # ========================================================================= # Machine features diff --git a/meta-digi-arm/conf/machine/include/ccmp1.inc b/meta-digi-arm/conf/machine/include/ccmp1.inc index 33229990f..46d3a9d73 100644 --- a/meta-digi-arm/conf/machine/include/ccmp1.inc +++ b/meta-digi-arm/conf/machine/include/ccmp1.inc @@ -77,8 +77,6 @@ IMAGE_FSTYPES:append = " ${@bb.utils.contains('BOOTDEVICE_LABELS', 'sdcard', 'bo # List of supported boot devices BOOTDEVICE_LABELS ?= "nand-4-256-1024 usb" -# Supported boot schemes -BOOTSCHEME_LABELS ?= "optee" # Remove NAND-based ubi/ubifs images added by st-machine-common-stm32mp.inc # because image_types_digi.bbclass already builds their own. From 3c6d3d356b2fa880b0c54d6045ccc847b5cb4b39 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Thu, 24 Apr 2025 13:20:46 +0200 Subject: [PATCH 073/138] ccmp1/ccmp2: clear ALSA_LIST with override and move it to include file ALSA_LIST variable is weakly set through an override (stm32mp1common or stm32mp2common). When the override is in place, the hard assignment of the plain variable is not enough, and the value weakly set in meta-st-stm32mp layer takes precedence over the plain variable hard assignment. Clear the variable with the override and move it from machine config files to the include file. Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/ccmp13-dvk.conf | 5 ----- meta-digi-arm/conf/machine/ccmp15-dvk.conf | 5 ----- meta-digi-arm/conf/machine/ccmp25-dvk.conf | 5 ----- meta-digi-arm/conf/machine/include/ccmp1.inc | 5 +++++ meta-digi-arm/conf/machine/include/ccmp2.inc | 5 +++++ 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/meta-digi-arm/conf/machine/ccmp13-dvk.conf b/meta-digi-arm/conf/machine/ccmp13-dvk.conf index 86bf5054a..405f76d5c 100644 --- a/meta-digi-arm/conf/machine/ccmp13-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp13-dvk.conf @@ -147,11 +147,6 @@ MACHINE_BLUETOOTH_EXTRA_INSTALL = "" XBEE_RESET_N_GPIO ?= "GPIOG@15" XBEE_TTY ?= "ttySTM2" -# ========================================================================= -# alsa -# ========================================================================= -ALSA_LIST = "" - # Per-machine DISTRO_FEATURES customization MACHINE_DISTRO_FEATURES_ADD = "efi optee" MACHINE_DISTRO_FEATURES_REMOVE = "gstreamer opengl vulkan wayland x11" diff --git a/meta-digi-arm/conf/machine/ccmp15-dvk.conf b/meta-digi-arm/conf/machine/ccmp15-dvk.conf index 89890f290..a5591dc79 100644 --- a/meta-digi-arm/conf/machine/ccmp15-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp15-dvk.conf @@ -158,11 +158,6 @@ MACHINE_BLUETOOTH_EXTRA_INSTALL = "" XBEE_RESET_N_GPIO ?= "GPIOZ@2" XBEE_TTY ?= "ttySTM2" -# ========================================================================= -# alsa -# ========================================================================= -ALSA_LIST = "" - # Per-machine DISTRO_FEATURES customization MACHINE_DISTRO_FEATURES_ADD = "efi optee" MACHINE_DISTRO_FEATURES_REMOVE = "vulkan x11" diff --git a/meta-digi-arm/conf/machine/ccmp25-dvk.conf b/meta-digi-arm/conf/machine/ccmp25-dvk.conf index b58f73999..a6420f48a 100644 --- a/meta-digi-arm/conf/machine/ccmp25-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp25-dvk.conf @@ -134,11 +134,6 @@ XBEE_RESET_N_GPIO ?= "GPIOD@6" XBEE_SLEEP_RQ_GPIO ?= "" XBEE_TTY ?= "ttySTM2" -# ========================================================================= -# alsa -# ========================================================================= -ALSA_LIST = "" - # Disable use of vendorfs and userfs partitions ST_VENDORFS = "0" ST_USERFS = "0" diff --git a/meta-digi-arm/conf/machine/include/ccmp1.inc b/meta-digi-arm/conf/machine/include/ccmp1.inc index 46d3a9d73..8ea9db1b2 100644 --- a/meta-digi-arm/conf/machine/include/ccmp1.inc +++ b/meta-digi-arm/conf/machine/include/ccmp1.inc @@ -71,6 +71,11 @@ MACHINE_EXTRA_RDEPENDS += " \ " MACHINE_EXTRA_RRECOMMENDS:remove = "kernel-imagebootfs" +# ========================================================================= +# alsa +# ========================================================================= +ALSA_LIST:stm32mp1common = "" + # Image FS types IMAGE_FSTYPES:remove = "ext4" IMAGE_FSTYPES:append = " ${@bb.utils.contains('BOOTDEVICE_LABELS', 'sdcard', 'boot.vfat ext4.gz', '', d)}" diff --git a/meta-digi-arm/conf/machine/include/ccmp2.inc b/meta-digi-arm/conf/machine/include/ccmp2.inc index a5020a602..6929110f5 100644 --- a/meta-digi-arm/conf/machine/include/ccmp2.inc +++ b/meta-digi-arm/conf/machine/include/ccmp2.inc @@ -85,6 +85,11 @@ MACHINE_EXTRA_RDEPENDS += " \ xbee-init \ " +# ========================================================================= +# alsa +# ========================================================================= +ALSA_LIST:stm32mp2common = "" + # Image FS types IMAGE_FSTYPES:remove = "ext4" From 9c3916da9491262577a4c9315bf481b0b9ec5291 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Fri, 25 Apr 2025 12:40:31 +0200 Subject: [PATCH 074/138] add INSANE_SKIP 32bit-time to avoid warnings on recipes using 32-bit APIs The binaries of certain recipes use 32-bit APIs (ioctl, stat) that produce build warnings. Add INSANE_SKIP to prevent the warnings. Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/include/ccmp1.inc | 6 ++++++ .../firmware-murata-infineon/firmware-murata-infineon.bb | 2 ++ 2 files changed, 8 insertions(+) diff --git a/meta-digi-arm/conf/machine/include/ccmp1.inc b/meta-digi-arm/conf/machine/include/ccmp1.inc index 8ea9db1b2..2ea163719 100644 --- a/meta-digi-arm/conf/machine/include/ccmp1.inc +++ b/meta-digi-arm/conf/machine/include/ccmp1.inc @@ -104,3 +104,9 @@ SWUPDATE_MTD_BLACKLIST = "0 1 2 3 4 5" # Remove custom kernel headers from toolchain ST_TOOLS_FOR_TARGET_SDK:remove = "kernel-headers" + +# Skip warnings for packages using 32-bit APIs +INSANE_SKIP:append:alsa-lib = " 32bit-time" +INSANE_SKIP:append:ir-keytable = " 32bit-time" +INSANE_SKIP:append:kbd = " 32bit-time" +INSANE_SKIP:append:v4l-utils = " 32bit-time" diff --git a/meta-digi-arm/recipes-bsp/firmware-murata-infineon/firmware-murata-infineon.bb b/meta-digi-arm/recipes-bsp/firmware-murata-infineon/firmware-murata-infineon.bb index 2efc433b7..bf0538a18 100644 --- a/meta-digi-arm/recipes-bsp/firmware-murata-infineon/firmware-murata-infineon.bb +++ b/meta-digi-arm/recipes-bsp/firmware-murata-infineon/firmware-murata-infineon.bb @@ -41,6 +41,8 @@ S = "${WORKDIR}" DEPENDS = "libnl" +INSANE_SKIP:append:stm32mp1common = " 32bit-time" + do_install () { bbnote "Installing Murata Infineon firmware binaries: " install -d ${D}${sbindir} From 4fc71f42e6b2deda0f533ff379765eef9774735d Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Mon, 28 Apr 2025 09:08:56 +0200 Subject: [PATCH 075/138] uboot: install_uuu: adapt to install TrustFence images - Add missing option '-t' - Change variable for signed suffix Signed-off-by: Hector Palacios --- .../u-boot/u-boot-dey/ccmp1/install_linux_fw_uuu.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_uuu.sh b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_uuu.sh index 70468cdd4..1f9f995ba 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_uuu.sh +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_uuu.sh @@ -1,7 +1,7 @@ #!/bin/sh #=============================================================================== # -# Copyright (C) 2022-2024 by Digi International Inc. +# Copyright (C) 2022-2025 by Digi International Inc. # All rights reserved. # # This program is free software; you can redistribute it and/or modify it @@ -84,7 +84,7 @@ echo "############################################################" # -b, -d, -n (booleans) # -f # -i -while getopts 'a:bdf:hi:n' c +while getopts 'a:bdf:hi:nt' c do case $c in a) INSTALL_ATF_FILENAME=${OPTARG} ;; @@ -128,12 +128,12 @@ echo "Determining image files to use..." # Determine ATF file to program if [ -z "${INSTALL_ATF_FILENAME}" ]; then - INSTALL_ATF_FILENAME="tf-a-##MACHINE##-${module_ram}-nand.stm32##SIGNED_TFA##" + INSTALL_ATF_FILENAME="tf-a-##MACHINE##-${module_ram}-optee-nand.stm32##SIGNED##" fi # Determine FIP file to program if [ -z "${INSTALL_FIP_FILENAME}" ]; then - INSTALL_FIP_FILENAME="fip-##MACHINE##-${module_ram}-optee##SIGNED##.bin" + INSTALL_FIP_FILENAME="fip-##MACHINE##-${module_ram}-optee-nand##SIGNED##.bin" fi # Determine linux, recovery, and rootfs image filenames to update From c728854f5cca2712081638fc28cc8f7c488f3b3f Mon Sep 17 00:00:00 2001 From: Gabriel Valcazar Date: Tue, 29 Apr 2025 11:34:19 +0200 Subject: [PATCH 076/138] Revert "stm32mp: gstreamer1.0-plugins-bad: revert STM patch to fix .flv video playback" After more in-depth testing, we discovered that the flv/ogv video issues on the ccmp25-dvk don't happen when playing videos with standard resolutions. Since the workaround consisted of reverting a patch backported from upstream gstreamer, and it only fixed flv video playback anyway, revert said workaround and test using videos with standard formats. This reverts commit e09eff7e1aa5e2b1b28d73c106d0b7cc9e17bf15. Signed-off-by: Gabriel Valcazar --- ...abuf-Handle-video-meta-inside-the-im.patch | 189 ------------------ .../gstreamer1.0-plugins-bad_1.22.12.bbappend | 5 - 2 files changed, 194 deletions(-) delete mode 100644 meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/Revert-wllinuxdmabuf-Handle-video-meta-inside-the-im.patch diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/Revert-wllinuxdmabuf-Handle-video-meta-inside-the-im.patch b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/Revert-wllinuxdmabuf-Handle-video-meta-inside-the-im.patch deleted file mode 100644 index 86b0ea42f..000000000 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/Revert-wllinuxdmabuf-Handle-video-meta-inside-the-im.patch +++ /dev/null @@ -1,189 +0,0 @@ -From: Gabriel Valcazar -Date: Fri, 11 Apr 2025 10:33:50 +0200 -Subject: [PATCH] Revert "wllinuxdmabuf: Handle video meta inside the importer" - -This commit is causing errors when playing .flv and .ogv files. Other formats -work as expected when reverting it - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Gabriel Valcazar ---- - ext/gtk/gstgtkwaylandsink.c | 24 +++++++++++++++++++----- - ext/wayland/gstwaylandsink.c | 20 +++++++++++++++++--- - gst-libs/gst/wayland/gstwllinuxdmabuf.c | 13 ++----------- - 3 files changed, 38 insertions(+), 19 deletions(-) - -diff --git a/ext/gtk/gstgtkwaylandsink.c b/ext/gtk/gstgtkwaylandsink.c -index 758cbfe..1396afe 100644 ---- a/ext/gtk/gstgtkwaylandsink.c -+++ b/ext/gtk/gstgtkwaylandsink.c -@@ -1156,7 +1156,9 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) - gst_gtk_wayland_sink_get_instance_private (self); - GstBuffer *to_render; - GstWlBuffer *wlbuffer; -+ GstVideoMeta *vmeta; - GstVideoFormat format; -+ GstVideoInfo src_vinfo; - GstMemory *mem; - struct wl_buffer *wbuf = NULL; - -@@ -1199,11 +1201,23 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) - /* update video info from video meta */ - mem = gst_buffer_peek_memory (buffer, 0); - -+ src_vinfo = priv->video_info; -+ vmeta = gst_buffer_get_video_meta (buffer); -+ if (vmeta) { -+ gint i; -+ -+ for (i = 0; i < vmeta->n_planes; i++) { -+ src_vinfo.offset[i] = vmeta->offset[i]; -+ src_vinfo.stride[i] = vmeta->stride[i]; -+ } -+ src_vinfo.size = gst_buffer_get_size (buffer); -+ } -+ - GST_LOG_OBJECT (self, - "buffer %" GST_PTR_FORMAT " does not have a wl_buffer from our " - "display, creating it", buffer); - -- format = GST_VIDEO_INFO_FORMAT (&priv->video_info); -+ format = GST_VIDEO_INFO_FORMAT (&src_vinfo); - if (gst_wl_display_check_format_for_dmabuf (priv->display, format)) { - guint i, nb_dmabuf = 0; - -@@ -1213,7 +1227,7 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) - - if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer))) - wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, priv->display, -- &priv->video_info); -+ &src_vinfo); - - if (wbuf) - GST_LOG_OBJECT (self, -@@ -1257,7 +1271,7 @@ gst_gtk_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) - GST_MAP_WRITE)) - goto dst_map_failed; - -- if (!gst_video_frame_map (&src, &priv->video_info, buffer, GST_MAP_READ)) { -+ if (!gst_video_frame_map (&src, &src_vinfo, buffer, GST_MAP_READ)) { - gst_video_frame_unmap (&dst); - goto src_map_failed; - } -@@ -1275,7 +1289,7 @@ handle_shm: - if (!wbuf && gst_wl_display_check_format_for_shm (priv->display, format)) { - if (gst_buffer_n_memory (buffer) == 1 && gst_is_fd_memory (mem)) - wbuf = gst_wl_shm_memory_construct_wl_buffer (mem, priv->display, -- &priv->video_info); -+ &src_vinfo); - - /* If nothing worked, copy into our internal pool */ - if (!wbuf) { -@@ -1316,7 +1330,7 @@ handle_shm: - GST_MAP_WRITE)) - goto dst_map_failed; - -- if (!gst_video_frame_map (&src, &priv->video_info, buffer, GST_MAP_READ)) { -+ if (!gst_video_frame_map (&src, &src_vinfo, buffer, GST_MAP_READ)) { - gst_video_frame_unmap (&dst); - goto src_map_failed; - } -diff --git a/ext/wayland/gstwaylandsink.c b/ext/wayland/gstwaylandsink.c -index c93fec9..b557ade 100644 ---- a/ext/wayland/gstwaylandsink.c -+++ b/ext/wayland/gstwaylandsink.c -@@ -862,7 +862,9 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) - GstWaylandSink *self = GST_WAYLAND_SINK (vsink); - GstBuffer *to_render; - GstWlBuffer *wlbuffer; -+ GstVideoMeta *vmeta; - GstVideoFormat format; -+ GstVideoInfo src_vinfo; - GstMemory *mem; - struct wl_buffer *wbuf = NULL; - -@@ -916,6 +918,18 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) - /* update video info from video meta */ - mem = gst_buffer_peek_memory (buffer, 0); - -+ src_vinfo = self->video_info; -+ vmeta = gst_buffer_get_video_meta (buffer); -+ if (vmeta) { -+ gint i; -+ -+ for (i = 0; i < vmeta->n_planes; i++) { -+ src_vinfo.offset[i] = vmeta->offset[i]; -+ src_vinfo.stride[i] = vmeta->stride[i]; -+ } -+ src_vinfo.size = gst_buffer_get_size (buffer); -+ } -+ - GST_LOG_OBJECT (self, - "buffer %" GST_PTR_FORMAT " does not have a wl_buffer from our " - "display, creating it", buffer); -@@ -930,7 +944,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) - - if (nb_dmabuf && (nb_dmabuf == gst_buffer_n_memory (buffer))) - wbuf = gst_wl_linux_dmabuf_construct_wl_buffer (buffer, self->display, -- &self->video_info); -+ &src_vinfo); - - if (wbuf) - GST_LOG_OBJECT (self, -@@ -974,7 +988,7 @@ gst_wayland_sink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) - GST_MAP_WRITE)) - goto dst_map_failed; - -- if (!gst_video_frame_map (&src, &self->video_info, buffer, GST_MAP_READ)) { -+ if (!gst_video_frame_map (&src, &src_vinfo, buffer, GST_MAP_READ)) { - gst_video_frame_unmap (&dst); - goto src_map_failed; - } -@@ -1032,7 +1046,7 @@ handle_shm: - GST_MAP_WRITE)) - goto dst_map_failed; - -- if (!gst_video_frame_map (&src, &self->video_info, buffer, GST_MAP_READ)) { -+ if (!gst_video_frame_map (&src, &src_vinfo, buffer, GST_MAP_READ)) { - gst_video_frame_unmap (&dst); - goto src_map_failed; - } -diff --git a/gst-libs/gst/wayland/gstwllinuxdmabuf.c b/gst-libs/gst/wayland/gstwllinuxdmabuf.c -index deb5d32..16b8fc1 100644 ---- a/gst-libs/gst/wayland/gstwllinuxdmabuf.c -+++ b/gst-libs/gst/wayland/gstwllinuxdmabuf.c -@@ -88,9 +88,6 @@ gst_wl_linux_dmabuf_construct_wl_buffer (GstBuffer * buf, - GstMemory *mem; - int format; - guint i, width, height; -- const gsize *offsets = info->offset; -- const gint *strides = info->stride; -- GstVideoMeta *vmeta; - guint nplanes, flags = 0; - struct zwp_linux_buffer_params_v1 *params; - gint64 timeout; -@@ -110,12 +107,6 @@ gst_wl_linux_dmabuf_construct_wl_buffer (GstBuffer * buf, - height = GST_VIDEO_INFO_HEIGHT (info); - nplanes = GST_VIDEO_INFO_N_PLANES (info); - -- vmeta = gst_buffer_get_video_meta (buf); -- if (vmeta) { -- offsets = vmeta->offset; -- strides = vmeta->stride; -- } -- - GST_DEBUG_OBJECT (display, "Creating wl_buffer from DMABuf of size %" - G_GSSIZE_FORMAT " (%d x %d), format %s", info->size, width, height, - gst_wl_dmabuf_format_to_string (format)); -@@ -128,8 +119,8 @@ gst_wl_linux_dmabuf_construct_wl_buffer (GstBuffer * buf, - guint offset, stride, mem_idx, length; - gsize skip; - -- offset = offsets[i]; -- stride = strides[i]; -+ offset = GST_VIDEO_INFO_PLANE_OFFSET (info, i); -+ stride = GST_VIDEO_INFO_PLANE_STRIDE (info, i); - if (gst_buffer_find_memory (buf, offset, 1, &mem_idx, &length, &skip)) { - GstMemory *m = gst_buffer_peek_memory (buf, mem_idx); - gint fd = gst_dmabuf_memory_get_fd (m); diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend index b300c1e42..95a873c4e 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.22.12.bbappend @@ -101,8 +101,3 @@ do_install:append() { install -d ${D}${includedir}/gstreamer-1.0/wayland install -m 644 ${S}/gst-libs/gst/wayland/wayland.h ${D}${includedir}/gstreamer-1.0/wayland } - -# DEY: revert STM patch nº8 to fix .flv video playback error -SRC_URI:append = " \ - file://Revert-wllinuxdmabuf-Handle-video-meta-inside-the-im.patch \ -" From 185651e6f024aab46abdf27abb31c2aabc336fde Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Tue, 29 Apr 2025 09:05:13 +0200 Subject: [PATCH 077/138] mkproject: re-insert ccmp1 platforms into build system The CCMP1 platforms were integrated as part of DEY-5.0, and this commit restores their support in the mkproject build system. Signed-off-by: Arturo Buzarra --- sdk/mkproject.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/mkproject.sh b/sdk/mkproject.sh index 2018054f6..354f3996c 100755 --- a/sdk/mkproject.sh +++ b/sdk/mkproject.sh @@ -28,7 +28,7 @@ MKP_NONE="\033[0m" MKP_CONFIGPATH="${MKP_SCRIPTPATH}/sources/meta-digi/meta-digi-arm/conf/templates" # Blacklist platforms (not officially supported in a DEY release) -MKP_BLACKLIST_PLATFORMS="ccimx6qpsbc ccimx6sbc ccimx8x-sbc-express ccmp13-dvk ccmp15-dvk" +MKP_BLACKLIST_PLATFORMS="ccimx6qpsbc ccimx6sbc ccimx8x-sbc-express" MKP_SETUP_ENVIRONMENT='#!/bin/bash From 9e6e94d84416685a1e58afa34752a80f0289c971 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Tue, 29 Apr 2025 09:08:35 +0200 Subject: [PATCH 078/138] ccmp15/ccmp13: fix BOOTABLE_ARTIFACTS for CCMP1 platforms In Yocto 5.0, the boot artifacts for CCMP1 platforms are compiled using the opteemin flavor. This commit updates the BOOTABLE_ARTIFACTS definition to reflect that change and properly integrate the boot artifacts into the ZIP installer. Signed-off-by: Arturo Buzarra --- meta-digi-arm/conf/machine/ccmp13-dvk.conf | 8 ++++++-- meta-digi-arm/conf/machine/ccmp15-dvk.conf | 14 ++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/meta-digi-arm/conf/machine/ccmp13-dvk.conf b/meta-digi-arm/conf/machine/ccmp13-dvk.conf index 405f76d5c..7b3504c62 100644 --- a/meta-digi-arm/conf/machine/ccmp13-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp13-dvk.conf @@ -62,6 +62,7 @@ OPTEE_DEVICETREE_optee = "${STM32MP_DEVICETREE}" OPTEE_DEVICETREE_opteemin = "${STM32MP_DEVICETREE}" # Supported boot schemes BOOTSCHEME_LABELS ?= "optee" +BOOTSCHEME_DEFAULT ?= "optee" # ========================================================================= # Machine features @@ -156,8 +157,11 @@ ST_VENDORFS = "0" # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = " \ - arm-trusted-firmware/tf-a-ccmp13-dvk-256MB-nand.stm32 \ - fip/fip-ccmp13-dvk-256MB-optee.bin \ + ${@oe.utils.ifelse(d.getVar('TRUSTFENCE_SIGN') == '1', 'tf-a-ccmp13-dvk-256MB-${BOOTSCHEME_DEFAULT}-nand${SIGN_SUFFIX}.stm32', \ + 'tf-a-ccmp13-dvk-256MB-${BOOTSCHEME_DEFAULT}-nand.stm32')} \ + metadata-ccmp13-dvk.bin \ + ${@oe.utils.ifelse(d.getVar('TRUSTFENCE_SIGN') == '1', 'fip-ccmp13-dvk-256MB-${BOOTSCHEME_DEFAULT}-nand${SIGN_SUFFIX}.bin', \ + 'fip-ccmp13-dvk-256MB-${BOOTSCHEME_DEFAULT}-nand.bin')} \ " # Default overlayfs_etc mount point and type diff --git a/meta-digi-arm/conf/machine/ccmp15-dvk.conf b/meta-digi-arm/conf/machine/ccmp15-dvk.conf index a5591dc79..6e20355d8 100644 --- a/meta-digi-arm/conf/machine/ccmp15-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp15-dvk.conf @@ -72,6 +72,7 @@ OPTEE_DEVICETREE_optee = "${STM32MP_DEVICETREE}" OPTEE_DEVICETREE_opteemin = "${STM32MP_DEVICETREE}" # Supported boot schemes BOOTSCHEME_LABELS ?= "opteemin" +BOOTSCHEME_DEFAULT ?= "opteemin" # ========================================================================= # Machine features @@ -168,10 +169,15 @@ ST_USERFS = "0" # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = " \ - arm-trusted-firmware/tf-a-ccmp15-dvk-512MB-nand.stm32 \ - arm-trusted-firmware/tf-a-ccmp15-dvk-1GB-nand.stm32 \ - fip/fip-ccmp15-dvk-512MB-optee.bin \ - fip/fip-ccmp15-dvk-1GB-optee.bin \ + ${@oe.utils.ifelse(d.getVar('TRUSTFENCE_SIGN') == '1', 'tf-a-ccmp15-dvk-512MB-${BOOTSCHEME_DEFAULT}-nand${SIGN_SUFFIX}.stm32', \ + 'tf-a-ccmp15-dvk-512MB-${BOOTSCHEME_DEFAULT}-nand.stm32')} \ + ${@oe.utils.ifelse(d.getVar('TRUSTFENCE_SIGN') == '1', 'tf-a-ccmp15-dvk-1GB-${BOOTSCHEME_DEFAULT}-nand${SIGN_SUFFIX}.stm32', \ + 'tf-a-ccmp15-dvk-1GB-${BOOTSCHEME_DEFAULT}-nand.stm32')} \ + metadata-ccmp15-dvk.bin \ + ${@oe.utils.ifelse(d.getVar('TRUSTFENCE_SIGN') == '1', 'fip-ccmp15-dvk-512MB-${BOOTSCHEME_DEFAULT}-nand${SIGN_SUFFIX}.bin', \ + 'fip-ccmp15-dvk-512MB-${BOOTSCHEME_DEFAULT}-nand.bin')} \ + ${@oe.utils.ifelse(d.getVar('TRUSTFENCE_SIGN') == '1', 'fip-ccmp15-dvk-1GB-${BOOTSCHEME_DEFAULT}-nand${SIGN_SUFFIX}.bin', \ + 'fip-ccmp15-dvk-1GB-${BOOTSCHEME_DEFAULT}-nand.bin')} \ " # Default overlayfs_etc mount point and type From 87aa304a2ca795d07ba0c2bc5519debb7e9ceeb0 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Tue, 29 Apr 2025 10:59:01 +0200 Subject: [PATCH 079/138] tf-a-stm32mp: use MACHINE variable for final metadata binary symlink Currently, the final metadata symlink is composed using the TF-A Device Tree configuration, which includes memory variant details. However, these variants are not relevant for the metadata binary. To avoid generating multiple redundant metadata files or using confusing names, this commit updates the symlink to be composed using the MACHINE variable instead. Signed-off-by: Arturo Buzarra --- .../recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend index 4e952c2f4..7f3e42581 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend @@ -332,7 +332,7 @@ deploy_symlinks_atf() { if [ -f "${DEPLOY_DIR_IMAGE}/${TF_A_BASEDIR}/${TF_A_METADATA_BINARY}" ]; then cd "${DEPLOY_DIR_IMAGE}" # symlink metadata - ln -sf "${TF_A_BASEDIR}/${TF_A_METADATA_BINARY}" "${DEPLOY_DIR_IMAGE}/${TF_A_METADATA_NAME}-${dt}.${TF_A_METADATA_SUFFIX}" + ln -sf "${TF_A_BASEDIR}/${TF_A_METADATA_BINARY}" "${DEPLOY_DIR_IMAGE}/${TF_A_METADATA_NAME}-${MACHINE}.${TF_A_METADATA_SUFFIX}" fi fi } From e87f7d0a5b8a1a7033b35bba3da80163bb2a7636 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Tue, 29 Apr 2025 11:12:08 +0200 Subject: [PATCH 080/138] u-boot-dey: ccmp1: refresh installer scripts for Yocto 5.0 support This commit updates the installer scripts to support CCMP1 platforms under Yocto 5.0, aligning them with the current behavior used for CCMP2. Changes include: - Adding support for the metadata partition, which is now required - Including the optee/opteemin flavors in boot artifact filenames - Ensuring the script structure and logic remain consistent with CCMP2 install scripts Signed-off-by: Arturo Buzarra --- .../recipes-bsp/u-boot/u-boot-dey.inc | 3 + .../u-boot-dey/ccmp1/install_linux_fw_sd.txt | 30 ++++++-- .../u-boot-dey/ccmp1/install_linux_fw_usb.txt | 30 ++++++-- .../u-boot-dey/ccmp1/install_linux_fw_uuu.sh | 70 ++++++++++--------- .../u-boot-dey/ccmp2/install_linux_fw_sd.txt | 2 +- .../u-boot-dey/ccmp2/install_linux_fw_usb.txt | 2 +- 6 files changed, 95 insertions(+), 42 deletions(-) diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc index 4d0edc6c9..4fc8bd759 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc @@ -85,6 +85,9 @@ build_uboot_scripts() { -e 's,##GRAPHICAL_IMAGES##,${GRAPHICAL_IMAGES},g' \ -e 's,##DEFAULT_IMAGE_NAME##,${DEFAULT_IMAGE_NAME},g' \ ${WORKDIR}/${f} > ${TMP_INSTALL_SCR} + if [ "${DEY_SOC_VENDOR}" = "STM" ]; then + sed -i -e 's,##BOOTSCHEME_DEFAULT##,${BOOTSCHEME_DEFAULT},g' ${TMP_INSTALL_SCR} + fi # Change the u-boot name when TrustFence is enabled if [ "${TRUSTFENCE_SIGN}" = "1" ]; then if [ "${DEY_SOC_VENDOR}" = "NXP" ]; then diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_sd.txt index 1b02a2b78..feb94734e 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_sd.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_sd.txt @@ -7,8 +7,9 @@ install_abort=0 BASEFILENAME=0 -setenv INSTALL_ATF_FILENAME tf-a-##MACHINE##-${module_ram}-nand.stm32 -setenv INSTALL_FIP_FILENAME fip-##MACHINE##-${module_ram}-optee.bin +setenv INSTALL_ATF_FILENAME tf-a-##MACHINE##-${module_ram}-##BOOTSCHEME_DEFAULT##-nand.stm32 +setenv INSTALL_METADATA_FILENAME metadata-##MACHINE##.bin +setenv INSTALL_FIP_FILENAME fip-##MACHINE##-${module_ram}-##BOOTSCHEME_DEFAULT##-nand.bin setenv INSTALL_MMCDEV 1 if test -z "${image-name}"; then @@ -28,7 +29,7 @@ setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.ubifs setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ubifs # Check for presence of firmware files on the SD card -FILES="${INSTALL_ATF_FILENAME} ${INSTALL_FIP_FILENAME} ${INSTALL_LINUX_FILENAME}" +FILES="${INSTALL_ATF_FILENAME} ${INSTALL_METADATA_FILENAME} ${INSTALL_FIP_FILENAME} ${INSTALL_LINUX_FILENAME}" if test "${dualboot}" != "yes"; then FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" fi @@ -68,6 +69,8 @@ echo " PARTITION FILENAME" echo " --------- --------" echo " fsbl1 ${INSTALL_ATF_FILENAME}" echo " fsbl2 ${INSTALL_ATF_FILENAME}" +echo " metadata1 ${INSTALL_METADATA_FILENAME}" +echo " metadata2 ${INSTALL_METADATA_FILENAME}" echo " fip-a ${INSTALL_FIP_FILENAME}" echo " fip-b ${INSTALL_FIP_FILENAME}" if test "${dualboot}" = "yes"; then @@ -101,7 +104,7 @@ setenv forced_update 1 # the first reset. setenv bootdelay 0 -# Update ATF and FIP images +# Update ATF, METADATA and FIP images echo "" echo "" echo ">> Installing ATF boot loader image ${INSTALL_ATF_FILENAME}" @@ -123,6 +126,25 @@ if test $? -eq 1; then fi echo "" echo "" +echo ">> Installing METADATA boot loader image ${INSTALL_METADATA_FILENAME}" +echo "" +echo "" +update metadata1 mmc ${INSTALL_MMCDEV} ${INSTALL_METADATA_FILENAME} +if test $? -eq 1; then + echo "[ERROR] Failed to update metadata1!"; + echo ""; + echo "Aborted."; + exit; +fi +update metadata2 mmc ${INSTALL_MMCDEV} ${INSTALL_METADATA_FILENAME} +if test $? -eq 1; then + echo "[ERROR] Failed to update metadata2!"; + echo ""; + echo "Aborted."; + exit; +fi +echo "" +echo "" echo ">> Installing FIP boot loader image ${INSTALL_FIP_FILENAME} (target will reset)" echo "" echo "" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_usb.txt index 73afcbffc..c491b64ee 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_usb.txt @@ -7,8 +7,9 @@ install_abort=0 BASEFILENAME=0 -setenv INSTALL_ATF_FILENAME tf-a-##MACHINE##-${module_ram}-nand.stm32 -setenv INSTALL_FIP_FILENAME fip-##MACHINE##-${module_ram}-optee.bin +setenv INSTALL_ATF_FILENAME tf-a-##MACHINE##-${module_ram}-##BOOTSCHEME_DEFAULT##-nand.stm32 +setenv INSTALL_METADATA_FILENAME metadata-##MACHINE##.bin +setenv INSTALL_FIP_FILENAME fip-##MACHINE##-${module_ram}-##BOOTSCHEME_DEFAULT##-nand.bin setenv INSTALL_USBDEV 0 if test -z "${image-name}"; then @@ -28,7 +29,7 @@ setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.ubifs setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ubifs # Check for presence of firmware files on the USB -FILES="${INSTALL_ATF_FILENAME} ${INSTALL_FIP_FILENAME} ${INSTALL_LINUX_FILENAME}" +FILES="${INSTALL_ATF_FILENAME} ${INSTALL_METADATA_FILENAME} ${INSTALL_FIP_FILENAME} ${INSTALL_LINUX_FILENAME}" if test "${dualboot}" != "yes"; then FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" fi @@ -70,6 +71,8 @@ echo " PARTITION FILENAME" echo " --------- --------" echo " fsbl1 ${INSTALL_ATF_FILENAME}" echo " fsbl2 ${INSTALL_ATF_FILENAME}" +echo " metadata1 ${INSTALL_METADATA_FILENAME}" +echo " metadata2 ${INSTALL_METADATA_FILENAME}" echo " fip-a ${INSTALL_FIP_FILENAME}" echo " fip-b ${INSTALL_FIP_FILENAME}" if test "${dualboot}" = "yes"; then @@ -103,7 +106,7 @@ setenv forced_update 1 # the first reset. setenv bootdelay 0 -# Update ATF and FIP images +# Update ATF, METADATA and FIP images echo "" echo "" echo ">> Installing ATF boot loader image ${INSTALL_ATF_FILENAME}" @@ -125,6 +128,25 @@ if test $? -eq 1; then fi echo "" echo "" +echo ">> Installing METADATA boot loader image ${INSTALL_METADATA_FILENAME}" +echo "" +echo "" +update metadata1 usb ${INSTALL_USBDEV} ${INSTALL_METADATA_FILENAME} +if test $? -eq 1; then + echo "[ERROR] Failed to update metadata1!"; + echo ""; + echo "Aborted."; + exit; +fi +update metadata2 usb ${INSTALL_USBDEV} ${INSTALL_METADATA_FILENAME} +if test $? -eq 1; then + echo "[ERROR] Failed to update metadata2!"; + echo ""; + echo "Aborted."; + exit; +fi +echo "" +echo "" echo ">> Installing FIP boot loader image ${INSTALL_FIP_FILENAME} (target will reset)" echo "" echo "" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_uuu.sh b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_uuu.sh index 1f9f995ba..031202e1e 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_uuu.sh +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_uuu.sh @@ -64,11 +64,11 @@ show_usage() # - runs 'update' command from RAM part_update() { - echo "\033[36m" - echo "=====================================================================================" - echo "Updating '${1}' partition with file: ${2}" - echo "=====================================================================================" - echo "\033[0m" + printf "\033[36m\n" + printf "=====================================================================================\n" + printf "Updating '%s' partition with file: %s\n" "${1}" "${2}" + printf "=====================================================================================\n" + printf "\033[0m\n" uuu fb: download -f "${2}" uuu "fb[-t ${3}]:" ucmd update "${1}" ram \${fastboot_buffer} \${filesize} ${ERASE} @@ -128,12 +128,13 @@ echo "Determining image files to use..." # Determine ATF file to program if [ -z "${INSTALL_ATF_FILENAME}" ]; then - INSTALL_ATF_FILENAME="tf-a-##MACHINE##-${module_ram}-optee-nand.stm32##SIGNED##" + INSTALL_ATF_FILENAME="tf-a-##MACHINE##-${module_ram}-##BOOTSCHEME_DEFAULT##-nand##SIGNED##.stm32" fi +INSTALL_METADATA_FILENAME="metadata-##MACHINE##.bin" # Determine FIP file to program if [ -z "${INSTALL_FIP_FILENAME}" ]; then - INSTALL_FIP_FILENAME="fip-##MACHINE##-${module_ram}-optee-nand##SIGNED##.bin" + INSTALL_FIP_FILENAME="fip-##MACHINE##-${module_ram}-##BOOTSCHEME_DEFAULT##-nand##SIGNED##.bin" fi # Determine linux, recovery, and rootfs image filenames to update @@ -154,27 +155,26 @@ INSTALL_RECOVERY_FILENAME="${BASEFILENAME}-##MACHINE##.recovery.ubifs" INSTALL_ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ubifs" # Verify existence of files before starting the update -FILES="${INSTALL_ATF_FILENAME} ${INSTALL_FIP_FILENAME} ${INSTALL_LINUX_FILENAME}" +FILES="${INSTALL_ATF_FILENAME} ${INSTALL_METADATA_FILENAME} ${INSTALL_FIP_FILENAME} ${INSTALL_LINUX_FILENAME}" if [ "${DUALBOOT}" != true ]; then FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" fi - for f in ${FILES}; do - if [ ! -f ${f} ]; then - echo "\033[31m[ERROR] Could not find file '${f}'\033[0m" + if [ ! -f "${f}" ]; then + printf "\033[31m[ERROR] Could not find file '%s'\033[0m\n" "${f}" ABORT=true fi done; # Verify what kind of rootfs is going to be programmed if [ ! -f ${INSTALL_ROOTFS_FILENAME} ]; then - echo "\033[31m[ERROR] Could not find file '${INSTALL_ROOTFS_FILENAME}'\033[0m" + printf "\033[31m[ERROR] Could not find file '${INSTALL_ROOTFS_FILENAME}'\033[0m\n" INSTALL_ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.squashfs" - echo "\033[32m[INFO] Trying with file '${INSTALL_ROOTFS_FILENAME}'\033[0m" + printf "\033[32m[INFO] Trying with file '${INSTALL_ROOTFS_FILENAME}'\033[0m\n" if [ -f "${INSTALL_ROOTFS_FILENAME}" ]; then SQUASHFS=true else - echo "\033[31m[ERROR] Could not find file '${INSTALL_ROOTFS_FILENAME}'\033[0m" + printf "\033[31m[ERROR] Could not find any rootfs image\033[0m\n" ABORT=true fi fi @@ -202,23 +202,25 @@ if [ "${NOWAIT}" != true ]; then printf "\n" printf " PARTITION\tFILENAME\n" printf " ---------\t--------\n" - printf " fsbl1\t${INSTALL_ATF_FILENAME}\n" - printf " fsbl2\t${INSTALL_ATF_FILENAME}\n" - printf " fip-a\t${INSTALL_FIP_FILENAME}\n" - printf " fip-b\t${INSTALL_FIP_FILENAME}\n" + printf " fsbl1\t%s\n" "${INSTALL_ATF_FILENAME}" + printf " fsbl2\t%s\n" "${INSTALL_ATF_FILENAME}" + printf " metadata1\t%s\n" "${INSTALL_METADATA_FILENAME}" + printf " metadata2\t%s\n" "${INSTALL_METADATA_FILENAME}" + printf " fip-a\t%s\n" "${INSTALL_FIP_FILENAME}" + printf " fip-b\t%s\n" "${INSTALL_FIP_FILENAME}" if [ "${DUALBOOT}" = true ]; then - printf " ${LINUX_NAME}_a\t${INSTALL_LINUX_FILENAME}\n" + printf " %s_a\t%s\n" "${LINUX_NAME}" "${INSTALL_LINUX_FILENAME}" if [ "${INSTALL_DUALBOOT}" = true ]; then - printf " ${LINUX_NAME}_b\t${INSTALL_LINUX_FILENAME}\n" + printf " %s_b\t%s\n" "${LINUX_NAME}" "${INSTALL_LINUX_FILENAME}" fi - printf " ${ROOTFS_NAME}_a\t${INSTALL_ROOTFS_FILENAME}\n" + printf " %s_a\t%s\n" "${ROOTFS_NAME}" "${INSTALL_ROOTFS_FILENAME}" if [ "${INSTALL_DUALBOOT}" = true ]; then - printf " ${ROOTFS_NAME}_b\t${INSTALL_ROOTFS_FILENAME}\n" + printf " %s_b\t%s\n" "${ROOTFS_NAME}" "${INSTALL_ROOTFS_FILENAME}" fi else - printf " ${LINUX_NAME}\t${INSTALL_LINUX_FILENAME}\n" - printf " ${RECOVERY_NAME}\t${INSTALL_RECOVERY_FILENAME}\n" - printf " ${ROOTFS_NAME}\t${INSTALL_ROOTFS_FILENAME}\n" + printf " %s\t%s\n" "${LINUX_NAME}" "${INSTALL_LINUX_FILENAME}" + printf " %s\t%s\n" "${RECOVERY_NAME}" "${INSTALL_RECOVERY_FILENAME}" + printf " %s\t%s\n" "${ROOTFS_NAME}" "${INSTALL_ROOTFS_FILENAME}" fi printf "\n" printf " Press CTRL+C now if you wish to abort.\n" @@ -226,7 +228,7 @@ if [ "${NOWAIT}" != true ]; then while [ ${WAIT} -gt 0 ]; do printf "\r Update process starts in %d " ${WAIT} sleep 1 - WAIT=$(( ${WAIT} - 1 )) + WAIT="$((WAIT - 1))" done printf "\r \n" printf " Starting update process\n" @@ -242,6 +244,10 @@ uuu fb: ucmd setenv forced_update 1 part_update "fsbl1" "${INSTALL_ATF_FILENAME}" 5000 part_update "fsbl2" "${INSTALL_ATF_FILENAME}" 5000 +# Update metadata +part_update "metadata1" "${INSTALL_METADATA_FILENAME}" 5000 +part_update "metadata2" "${INSTALL_METADATA_FILENAME}" 5000 + # Update FIP part_update "fip-a" "${INSTALL_FIP_FILENAME}" 5000 part_update "fip-b" "${INSTALL_FIP_FILENAME}" 5000 @@ -315,7 +321,7 @@ else fi # Set the dboot_kernel_var to fitimage if Trustfence is enabled -if [ "${TRUSTFENCE}" = "true" ] || echo "$INSTALL_UBOOT_FILENAME" | grep -q -e "signed"; then +if [ "${TRUSTFENCE}" = "true" ] || echo "${INSTALL_FIP_FILENAME}" | grep -q -e "Signed"; then uuu fb: ucmd setenv dboot_kernel_var fitimage uuu fb: ucmd saveenv fi @@ -330,10 +336,10 @@ uuu fb: ucmd bootcount reset # Reset the target uuu fb: acmd reset -echo "\033[32m" -echo "=============================================================" -echo "Done! Wait for the target to complete first boot process." -echo "=============================================================" -echo "\033[0m" +printf "\033[32m\n" +printf "=============================================================\n" +printf "Done! Wait for the target to complete first boot process.\n" +printf "=============================================================\n" +printf "\033[0m\n" exit diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_sd.txt index 958bc1da0..46b3df7fe 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_sd.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_sd.txt @@ -127,7 +127,7 @@ if test $? -eq 1; then fi echo "" echo "" -echo ">> Installing METADATA boot loader image ${INSTALL_ATF_FILENAME}" +echo ">> Installing METADATA boot loader image ${INSTALL_METADATA_FILENAME}" echo "" echo "" update metadata1 mmc ${INSTALL_MMCDEV} ${INSTALL_METADATA_FILENAME} diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_usb.txt index 7c7e97106..ae5bd9ae7 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_usb.txt @@ -127,7 +127,7 @@ if test $? -eq 1; then fi echo "" echo "" -echo ">> Installing METADATA boot loader image ${INSTALL_ATF_FILENAME}" +echo ">> Installing METADATA boot loader image ${INSTALL_METADATA_FILENAME}" echo "" echo "" update metadata1 usb ${INSTALL_USBDEV} ${INSTALL_METADATA_FILENAME} From 63034bf86e25e897ec82edf66f9918955a23a644 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Tue, 6 May 2025 15:23:50 +0200 Subject: [PATCH 081/138] imx-vpu-hantro: delete overlayed recipes meta-freescale provides newer versions of those recipes. https://onedigi.atlassian.net/browse/DEL-9566 Signed-off-by: Javier Viguera --- .../imx-vpu-hantro-daemon_1.2.0.bb | 38 ------------------ .../imx-vpu-hantro/imx-vpu-hantro_1.33.0.bb | 40 ------------------- 2 files changed, 78 deletions(-) delete mode 100644 meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-vpu-hantro/imx-vpu-hantro-daemon_1.2.0.bb delete mode 100644 meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-vpu-hantro/imx-vpu-hantro_1.33.0.bb diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-vpu-hantro/imx-vpu-hantro-daemon_1.2.0.bb b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-vpu-hantro/imx-vpu-hantro-daemon_1.2.0.bb deleted file mode 100644 index 6635160d4..000000000 --- a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-vpu-hantro/imx-vpu-hantro-daemon_1.2.0.bb +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2021-2024 NXP -DESCRIPTION = "i.MX Hantro V4L2 Daemon" -LICENSE = "MIT" -LIC_FILES_CHKSUM = "file://LICENSE.txt;md5=cd8bc2a79509c22fc9c1782a151210b1" - -DEPENDS = "imx-vpu-hantro" -DEPENDS:append:mx8mp-nxp-bsp = " imx-vpu-hantro-vc" - -SRC_URI = "${FSL_MIRROR}/${BP}-${IMX_SRCREV_ABBREV}.tar.gz" -IMX_SRCREV_ABBREV = "67fa7a7" - -SRC_URI[md5sum] = "f9f3b18b33afdc1ef4d6cbef7edc8a54" -SRC_URI[sha256sum] = "79aecdf7fbeb90ca7322a6dbc61465d1d2996f6fa5d880019066cc873621238f" - -S = "${WORKDIR}/${BP}-${IMX_SRCREV_ABBREV}" - -PLATFORM:mx8mm-nxp-bsp = "IMX8MM" -PLATFORM:mx8mq-nxp-bsp = "IMX8MQ" -PLATFORM:mx8mp-nxp-bsp = "IMX8MP" - -EXTRA_OEMAKE = " \ - CROSS_COMPILE="${HOST_PREFIX}" \ - SDKTARGETSYSROOT="${STAGING_DIR_TARGET}" \ - CTRLSW_HDRPATH="${STAGING_INCDIR}" \ - PLATFORM="${PLATFORM}" \ -" - -do_compile () { - oe_runmake clean - oe_runmake PLATFORM="${PLATFORM}" all -} - -do_install () { - oe_runmake install DEST_DIR="${D}" -} - -PACKAGE_ARCH = "${MACHINE_SOCARCH}" -COMPATIBLE_MACHINE = "(mx8mq-nxp-bsp|mx8mm-nxp-bsp|mx8mp-nxp-bsp)" diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-vpu-hantro/imx-vpu-hantro_1.33.0.bb b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-vpu-hantro/imx-vpu-hantro_1.33.0.bb deleted file mode 100644 index 510050cff..000000000 --- a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/imx-vpu-hantro/imx-vpu-hantro_1.33.0.bb +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2017-2020,2023-2024 NXP - -DESCRIPTION = "i.MX Hantro VPU library" -LICENSE = "Proprietary" -LIC_FILES_CHKSUM = "file://COPYING;md5=10c0fda810c63b052409b15a5445671a" - -PROVIDES = "virtual/imxvpu" - -SRC_URI = "${FSL_MIRROR}/${BP}-${IMX_SRCREV_ABBREV}.bin;fsl-eula=true" -IMX_SRCREV_ABBREV = "7a434fc" - -SRC_URI[md5sum] = "ed9d128727327e3a1a227fab9760ceff" -SRC_URI[sha256sum] = "1c2f78444051ef0c7e6107e1ddb082791d987d203f94f20f08a84a289a8eb984" - -S = "${WORKDIR}/${BP}-${IMX_SRCREV_ABBREV}" - -inherit fsl-eula-unpack use-imx-headers - -PARALLEL_MAKE="-j 1" - -PLATFORM:mx8mm-nxp-bsp = "IMX8MM" -PLATFORM:mx8mq-nxp-bsp = "IMX8MQ" -PLATFORM:mx8mp-nxp-bsp = "IMX8MP" - -EXTRA_OEMAKE = " \ - CROSS_COMPILE="${HOST_PREFIX}" \ - SDKTARGETSYSROOT="${STAGING_DIR_TARGET}" \ - PLATFORM="${PLATFORM}" \ -" - -do_install () { - oe_runmake install DEST_DIR="${D}" -} - -FILES:${PN} += "/unit_tests" - -RDEPENDS:${PN} += "imx-vpu-hantro-daemon" - -PACKAGE_ARCH = "${MACHINE_SOCARCH}" -COMPATIBLE_MACHINE = "(mx8mq-nxp-bsp|mx8mm-nxp-bsp|mx8mp-nxp-bsp)" From 1032a127e2f71c85b84adfde3300aa2dcd913672 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Tue, 6 May 2025 15:24:21 +0200 Subject: [PATCH 082/138] meta-digi: update i.MX GStreamer stack to 1.24.7.imx NXP uses this version for its 6.6.52-2.2.0 release. https://onedigi.atlassian.net/browse/DEL-9566 Signed-off-by: Javier Viguera --- .../conf/machine/include/imx-digi-base.inc | 8 +- ...issing-opencv-data-dir-in-yocto-buil.patch | 33 +++ .../gstreamer1.0-plugins-bad_1.24.7.imx.bb | 224 ++++++++++++++++++ .../gstreamer1.0-plugins-base_1.24.7.imx.bb | 146 ++++++++++++ ...reamer1.0-plugins-base_1.24.7.imx.bbappend | 17 ++ .../gstreamer1.0-plugins-good_1.24.7.imx.bb | 121 ++++++++++ .../gstreamer/gstreamer1.0/run-ptest | 2 + .../gstreamer/gstreamer1.0_1.24.7.imx.bb | 112 +++++++++ ...lugin-fix-build-using-MUSL-C-library.patch | 36 --- .../gstreamer/imx-gst1.0-plugin_%.bbappend | 8 +- 10 files changed, 665 insertions(+), 42 deletions(-) create mode 100644 meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0004-opencv-resolve-missing-opencv-data-dir-in-yocto-buil.patch create mode 100644 meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.24.7.imx.bb create mode 100644 meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base_1.24.7.imx.bb create mode 100644 meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base_1.24.7.imx.bbappend create mode 100644 meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-good_1.24.7.imx.bb create mode 100755 meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0/run-ptest create mode 100644 meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0_1.24.7.imx.bb delete mode 100644 meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/imx-gst1.0-plugin/0002-imx-gst1.0-plugin-fix-build-using-MUSL-C-library.patch diff --git a/meta-digi-arm/conf/machine/include/imx-digi-base.inc b/meta-digi-arm/conf/machine/include/imx-digi-base.inc index 72b52363e..2824b8fa2 100644 --- a/meta-digi-arm/conf/machine/include/imx-digi-base.inc +++ b/meta-digi-arm/conf/machine/include/imx-digi-base.inc @@ -244,10 +244,10 @@ MACHINE_GSTREAMER_1_0_PLUGIN:mx9-nxp-bsp ?= "imx-gst1.0-plugin" # to 'imx-nxp-bsp' # # GStreamer forked recipes -PREFERRED_VERSION_gstreamer1.0:imx-nxp-bsp ??= "1.24.0.imx" -PREFERRED_VERSION_gstreamer1.0-plugins-bad:imx-nxp-bsp ??= "1.24.0.imx" -PREFERRED_VERSION_gstreamer1.0-plugins-base:imx-nxp-bsp ??= "1.24.0.imx" -PREFERRED_VERSION_gstreamer1.0-plugins-good:imx-nxp-bsp ??= "1.24.0.imx" +PREFERRED_VERSION_gstreamer1.0:imx-nxp-bsp ??= "1.24.7.imx" +PREFERRED_VERSION_gstreamer1.0-plugins-bad:imx-nxp-bsp ??= "1.24.7.imx" +PREFERRED_VERSION_gstreamer1.0-plugins-base:imx-nxp-bsp ??= "1.24.7.imx" +PREFERRED_VERSION_gstreamer1.0-plugins-good:imx-nxp-bsp ??= "1.24.7.imx" # GStreamer copied recipes PREFERRED_VERSION_gst-devtools:imx-nxp-bsp ??= "1.22.5.imx" diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0004-opencv-resolve-missing-opencv-data-dir-in-yocto-buil.patch b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0004-opencv-resolve-missing-opencv-data-dir-in-yocto-buil.patch new file mode 100644 index 000000000..029b80e17 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0004-opencv-resolve-missing-opencv-data-dir-in-yocto-buil.patch @@ -0,0 +1,33 @@ +From 3bc5d48257032b6bbee532aad15062fbbcc43bfe Mon Sep 17 00:00:00 2001 +From: Andrey Zhizhikin +Date: Mon, 27 Jan 2020 10:22:35 +0000 +Subject: [PATCH] opencv: resolve missing opencv data dir in yocto build + +When Yocto build is performed, opencv searches for data dir using simple +'test' command, this fails because pkg-config provides an absolute +path on the target which needs to be prepended by PKG_CONFIG_SYSROOT_DIR +in order for the 'test' utility to pick up the absolute path. + +Upstream-Status: Inappropriate [OE-specific] + +Signed-off-by: Andrey Zhizhikin +Signed-off-by: Jose Quaresma + +--- + ext/opencv/meson.build | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/ext/opencv/meson.build b/ext/opencv/meson.build +index 1d86b90..b5c8b95 100644 +--- a/ext/opencv/meson.build ++++ b/ext/opencv/meson.build +@@ -87,6 +87,9 @@ if opencv_found + opencv_prefix = opencv_dep.get_variable('prefix') + gstopencv_cargs += ['-DOPENCV_PREFIX="' + opencv_prefix + '"'] + ++ pkgconf_sysroot = run_command(python3, '-c', 'import os; print(os.environ.get("PKG_CONFIG_SYSROOT_DIR"))').stdout().strip() ++ opencv_prefix = pkgconf_sysroot + opencv_prefix ++ + # Check the data dir used by opencv for its xml data files + # Use prefix from pkg-config to be compatible with cross-compilation + r = run_command('test', '-d', opencv_prefix + '/share/opencv', check: false) diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.24.7.imx.bb b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.24.7.imx.bb new file mode 100644 index 000000000..cbf2101c7 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad_1.24.7.imx.bb @@ -0,0 +1,224 @@ +# This recipe is for the i.MX fork of gstreamer1.0-plugins-bad. For ease of +# maintenance, the top section is a verbatim copy of an OE-core +# recipe. The second section customizes the recipe for i.MX. + +########### OE-core copy ################## +# Upstream hash: 937817e5164f8af8452aec03ae3c45cb23d63df9 + +require recipes-multimedia/gstreamer/gstreamer1.0-plugins-common.inc +require recipes-multimedia/gstreamer/gstreamer1.0-plugins-license.inc + +SUMMARY = "'Bad' GStreamer plugins and helper libraries " +HOMEPAGE = "https://gstreamer.freedesktop.org/" +BUGTRACKER = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues" + +SRC_URI = "https://gstreamer.freedesktop.org/src/gst-plugins-bad/gst-plugins-bad-${PV}.tar.xz \ + file://0001-fix-maybe-uninitialized-warnings-when-compiling-with.patch \ + file://0002-avoid-including-sys-poll.h-directly.patch \ + file://0004-opencv-resolve-missing-opencv-data-dir-in-yocto-buil.patch \ + " +SRC_URI[sha256sum] = "e64e75cdafd7ff2fc7fc34e855b06b1e3ed227cc06fa378d17bbcd76780c338c" + +S = "${WORKDIR}/gst-plugins-bad-${PV}" + +LICENSE = "LGPL-2.1-or-later & GPL-2.0-or-later" +LIC_FILES_CHKSUM = "file://COPYING;md5=4fbd65380cdd255951079008b364516c" + +DEPENDS += "gstreamer1.0-plugins-base" + +inherit gobject-introspection + +PACKAGECONFIG ??= " \ + ${GSTREAMER_ORC} \ + ${@bb.utils.contains('DISTRO_FEATURES', 'bluetooth', 'bluez', '', d)} \ + ${@bb.utils.filter('DISTRO_FEATURES', 'directfb vulkan x11', d)} \ + ${@bb.utils.contains('DISTRO_FEATURES', 'wayland', 'wayland', '', d)} \ + ${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'gl', '', d)} \ + bz2 closedcaption curl dash dtls hls openssl sbc smoothstreaming \ + sndfile ttml uvch264 webp \ + ${@bb.utils.contains('TUNE_FEATURES', 'mx32', '', 'rsvg', d)} \ +" + +PACKAGECONFIG[aom] = "-Daom=enabled,-Daom=disabled,aom" +PACKAGECONFIG[assrender] = "-Dassrender=enabled,-Dassrender=disabled,libass" +PACKAGECONFIG[avtp] = "-Davtp=enabled,-Davtp=disabled,libavtp" +PACKAGECONFIG[bluez] = "-Dbluez=enabled,-Dbluez=disabled,bluez5" +PACKAGECONFIG[bz2] = "-Dbz2=enabled,-Dbz2=disabled,bzip2" +PACKAGECONFIG[closedcaption] = "-Dclosedcaption=enabled,-Dclosedcaption=disabled,pango cairo" +PACKAGECONFIG[curl] = "-Dcurl=enabled,-Dcurl=disabled,curl" +PACKAGECONFIG[dash] = "-Ddash=enabled,-Ddash=disabled,libxml2" +PACKAGECONFIG[dc1394] = "-Ddc1394=enabled,-Ddc1394=disabled,libdc1394" +PACKAGECONFIG[directfb] = "-Ddirectfb=enabled,-Ddirectfb=disabled,directfb" +PACKAGECONFIG[dtls] = "-Ddtls=enabled,-Ddtls=disabled,openssl" +PACKAGECONFIG[faac] = "-Dfaac=enabled,-Dfaac=disabled,faac" +PACKAGECONFIG[faad] = "-Dfaad=enabled,-Dfaad=disabled,faad2" +PACKAGECONFIG[fluidsynth] = "-Dfluidsynth=enabled,-Dfluidsynth=disabled,fluidsynth" +PACKAGECONFIG[hls] = "-Dhls=enabled,-Dhls=disabled," +# Pick atleast one crypto backend below when enabling hls +PACKAGECONFIG[nettle] = "-Dhls-crypto=nettle,,nettle" +PACKAGECONFIG[openssl] = "-Dhls-crypto=openssl,,openssl" +PACKAGECONFIG[gcrypt] = "-Dhls-crypto=libgcrypt,,libgcrypt" +# the gl packageconfig enables OpenGL elements that haven't been ported +# to -base yet. They depend on the gstgl library in -base, so we do +# not add GL dependencies here, since these are taken care of in -base. +PACKAGECONFIG[gl] = "-Dgl=enabled,-Dgl=disabled," +PACKAGECONFIG[kms] = "-Dkms=enabled,-Dkms=disabled,libdrm" +PACKAGECONFIG[libde265] = "-Dlibde265=enabled,-Dlibde265=disabled,libde265" +PACKAGECONFIG[libssh2] = "-Dcurl-ssh2=enabled,-Dcurl-ssh2=disabled,libssh2" +PACKAGECONFIG[lcms2] = "-Dcolormanagement=enabled,-Dcolormanagement=disabled,lcms" +PACKAGECONFIG[modplug] = "-Dmodplug=enabled,-Dmodplug=disabled,libmodplug" +PACKAGECONFIG[msdk] = "-Dmsdk=enabled -Dmfx_api=oneVPL,-Dmsdk=disabled,onevpl-intel-gpu" +PACKAGECONFIG[neon] = "-Dneon=enabled,-Dneon=disabled,neon" +PACKAGECONFIG[openal] = "-Dopenal=enabled,-Dopenal=disabled,openal-soft" +PACKAGECONFIG[opencv] = "-Dopencv=enabled,-Dopencv=disabled,opencv" +PACKAGECONFIG[openh264] = "-Dopenh264=enabled,-Dopenh264=disabled,openh264" +PACKAGECONFIG[openjpeg] = "-Dopenjpeg=enabled,-Dopenjpeg=disabled,openjpeg" +PACKAGECONFIG[openmpt] = "-Dopenmpt=enabled,-Dopenmpt=disabled,libopenmpt" +# the opus encoder/decoder elements are now in the -base package, +# but the opus parser remains in -bad +PACKAGECONFIG[opusparse] = "-Dopus=enabled,-Dopus=disabled,libopus" +PACKAGECONFIG[resindvd] = "-Dresindvd=enabled,-Dresindvd=disabled,libdvdread libdvdnav" +PACKAGECONFIG[rsvg] = "-Drsvg=enabled,-Drsvg=disabled,librsvg" +PACKAGECONFIG[rtmp] = "-Drtmp=enabled,-Drtmp=disabled,rtmpdump" +PACKAGECONFIG[sbc] = "-Dsbc=enabled,-Dsbc=disabled,sbc" +PACKAGECONFIG[sctp] = "-Dsctp=enabled,-Dsctp=disabled" +PACKAGECONFIG[smoothstreaming] = "-Dsmoothstreaming=enabled,-Dsmoothstreaming=disabled,libxml2" +PACKAGECONFIG[sndfile] = "-Dsndfile=enabled,-Dsndfile=disabled,libsndfile1" +PACKAGECONFIG[srt] = "-Dsrt=enabled,-Dsrt=disabled,srt" +PACKAGECONFIG[srtp] = "-Dsrtp=enabled,-Dsrtp=disabled,libsrtp" +PACKAGECONFIG[tinyalsa] = "-Dtinyalsa=enabled,-Dtinyalsa=disabled,tinyalsa" +PACKAGECONFIG[ttml] = "-Dttml=enabled,-Dttml=disabled,libxml2 pango cairo" +PACKAGECONFIG[uvch264] = "-Duvch264=enabled,-Duvch264=disabled,libusb1 libgudev" +# this enables support for stateless V4L2 mem2mem codecs, which is a newer form of +# V4L2 codec; the V4L2 code in -base supports the older stateful V4L2 mem2mem codecs +PACKAGECONFIG[v4l2codecs] = "-Dv4l2codecs=enabled,-Dv4l2codecs=disabled,libgudev" +PACKAGECONFIG[va] = "-Dva=enabled,-Dva=disabled,libva" +PACKAGECONFIG[voaacenc] = "-Dvoaacenc=enabled,-Dvoaacenc=disabled,vo-aacenc" +PACKAGECONFIG[voamrwbenc] = "-Dvoamrwbenc=enabled,-Dvoamrwbenc=disabled,vo-amrwbenc" +PACKAGECONFIG[vulkan] = "-Dvulkan=enabled,-Dvulkan=disabled,vulkan-loader shaderc-native" +PACKAGECONFIG[wayland] = "-Dwayland=enabled,-Dwayland=disabled,wayland-native wayland wayland-protocols libdrm" +PACKAGECONFIG[webp] = "-Dwebp=enabled,-Dwebp=disabled,libwebp" +PACKAGECONFIG[webrtc] = "-Dwebrtc=enabled,-Dwebrtc=disabled,libnice" +PACKAGECONFIG[webrtcdsp] = "-Dwebrtcdsp=enabled,-Dwebrtcdsp=disabled,webrtc-audio-processing" +PACKAGECONFIG[zbar] = "-Dzbar=enabled,-Dzbar=disabled,zbar" +PACKAGECONFIG[x11] = "-Dx11=enabled,-Dx11=disabled,libxcb libxkbcommon" +PACKAGECONFIG[x265] = "-Dx265=enabled,-Dx265=disabled,x265" + +GSTREAMER_GPL = "${@bb.utils.filter('PACKAGECONFIG', 'faad resindvd x265', d)}" + +EXTRA_OEMESON += " \ + -Ddoc=disabled \ + -Daes=enabled \ + -Dcodecalpha=enabled \ + -Ddecklink=enabled \ + -Ddvb=enabled \ + -Dfbdev=enabled \ + -Dipcpipeline=enabled \ + -Dshm=enabled \ + -Dtranscode=enabled \ + -Dandroidmedia=disabled \ + -Dapplemedia=disabled \ + -Dasio=disabled \ + -Dbs2b=disabled \ + -Dchromaprint=disabled \ + -Dd3dvideosink=disabled \ + -Dd3d11=disabled \ + -Ddirectsound=disabled \ + -Ddts=disabled \ + -Dfdkaac=disabled \ + -Dflite=disabled \ + -Dgme=disabled \ + -Dgs=disabled \ + -Dgsm=disabled \ + -Diqa=disabled \ + -Dkate=disabled \ + -Dladspa=disabled \ + -Dldac=disabled \ + -Dlv2=disabled \ + -Dmagicleap=disabled \ + -Dmediafoundation=disabled \ + -Dmicrodns=disabled \ + -Dmpeg2enc=disabled \ + -Dmplex=disabled \ + -Dmusepack=disabled \ + -Dnvcodec=disabled \ + -Dopenexr=disabled \ + -Dopenni2=disabled \ + -Dopenaptx=disabled \ + -Dopensles=disabled \ + -Donnx=disabled \ + -Dqroverlay=disabled \ + -Dsoundtouch=disabled \ + -Dspandsp=disabled \ + -Dsvthevcenc=disabled \ + -Dteletext=disabled \ + -Dwasapi=disabled \ + -Dwasapi2=disabled \ + -Dwildmidi=disabled \ + -Dwinks=disabled \ + -Dwinscreencap=disabled \ + -Dwpe=disabled \ + -Dzxing=disabled \ +" + +export OPENCV_PREFIX = "${STAGING_DIR_TARGET}${prefix}" + +ARM_INSTRUCTION_SET:armv4 = "arm" +ARM_INSTRUCTION_SET:armv5 = "arm" + +FILES:${PN}-freeverb += "${datadir}/gstreamer-1.0/presets/GstFreeverb.prs" +FILES:${PN}-opencv += "${datadir}/gst-plugins-bad/1.0/opencv*" +FILES:${PN}-transcode += "${datadir}/gstreamer-1.0/encoding-profiles" +FILES:${PN}-voamrwbenc += "${datadir}/gstreamer-1.0/presets/GstVoAmrwbEnc.prs" + + +########### End of OE-core copy ########### + +########### i.MX overrides ################ + +DEFAULT_PREFERENCE = "-1" + +LIC_FILES_CHKSUM = "file://LICENSE.txt;md5=69333daa044cb77e486cc36129f7a770" + +SRC_URI:remove = "https://gstreamer.freedesktop.org/src/gst-plugins-bad/gst-plugins-bad-${PV}.tar.xz \ + file://0001-fix-maybe-uninitialized-warnings-when-compiling-with.patch \ + file://0002-avoid-including-sys-poll.h-directly.patch \ + file://0004-opencv-resolve-missing-opencv-data-dir-in-yocto-buil.patch \ + " +SRC_URI:prepend = "${GST1.0-PLUGINS-BAD_SRC};branch=${SRCBRANCH} " +GST1.0-PLUGINS-BAD_SRC ?= "gitsm://github.com/nxp-imx/gst-plugins-bad.git;protocol=https" +SRCBRANCH = "MM_04.09.02_2410_L6.6.y" +SRCREV = "bba296b75dfb3e7ae4c836255edbd2b3c085c269" + +S = "${WORKDIR}/git" + +inherit use-imx-headers + +PACKAGE_ARCH:imxpxp = "${MACHINE_SOCARCH}" +PACKAGE_ARCH:mx8-nxp-bsp = "${MACHINE_SOCARCH}" + +PACKAGECONFIG_REMOVE ?= " \ + dtls vulkan \ + ${@bb.utils.contains('DISTRO_FEATURES', 'wayland', '', 'gl', d)} \ +" +PACKAGECONFIG:remove = "${PACKAGECONFIG_REMOVE}" +PACKAGECONFIG:append:mx8-nxp-bsp = " kms tinycompress" + +PACKAGECONFIG:append = " ${PACKAGECONFIG_G2D}" +PACKAGECONFIG_G2D ??= "" +PACKAGECONFIG_G2D:imxgpu2d ??= "g2d" + +PACKAGECONFIG[g2d] = ",,virtual/libg2d" +PACKAGECONFIG[tinycompress] = "-Dtinycompress=enabled,-Dtinycompress=disabled,tinycompress" + +EXTRA_OEMESON += " \ + -Dc_args="${CFLAGS} -I${STAGING_INCDIR_IMX}" \ +" + +EXTRA_OEMESON:remove = " \ + -Dkate=disabled \ +" + +COMPATIBLE_MACHINE = "(imx-nxp-bsp)" + +########### End of i.MX overrides ######### diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base_1.24.7.imx.bb b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base_1.24.7.imx.bb new file mode 100644 index 000000000..f1f69c173 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base_1.24.7.imx.bb @@ -0,0 +1,146 @@ +# This recipe is for the i.MX fork of gstreamer1.0-plugins-base. For ease of +# maintenance, the top section is a verbatim copy of an OE-core +# recipe. The second section customizes the recipe for i.MX. + +########### OE-core copy ################## +# Upstream hash: 937817e5164f8af8452aec03ae3c45cb23d63df9 + +require recipes-multimedia/gstreamer/gstreamer1.0-plugins-common.inc + +SUMMARY = "'Base' GStreamer plugins and helper libraries" +HOMEPAGE = "https://gstreamer.freedesktop.org/" +BUGTRACKER = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/issues" +LICENSE = "LGPL-2.1-or-later" +LIC_FILES_CHKSUM = "file://COPYING;md5=69333daa044cb77e486cc36129f7a770" + +SRC_URI = "https://gstreamer.freedesktop.org/src/gst-plugins-base/gst-plugins-base-${PV}.tar.xz \ + file://0001-ENGR00312515-get-caps-from-src-pad-when-query-caps.patch \ + file://0003-viv-fb-Make-sure-config.h-is-included.patch \ + file://0002-ssaparse-enhance-SSA-text-lines-parsing.patch \ + " +SRC_URI[sha256sum] = "edd4338b45c26a9af28c0d35aab964a024c3884ba6f520d8428df04212c8c93a" + +S = "${WORKDIR}/gst-plugins-base-${PV}" + +DEPENDS += "iso-codes util-linux zlib" + +inherit gobject-introspection + +# opengl packageconfig factored out to make it easy for distros +# and BSP layers to choose OpenGL APIs/platforms/window systems +PACKAGECONFIG_X11 = "${@bb.utils.contains('DISTRO_FEATURES', 'x11', 'opengl glx', '', d)}" +PACKAGECONFIG_GL ?= "${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'gles2 egl ${PACKAGECONFIG_X11}', '', d)}" + +PACKAGECONFIG ??= " \ + ${GSTREAMER_ORC} \ + ${PACKAGECONFIG_GL} \ + ${@bb.utils.filter('DISTRO_FEATURES', 'alsa x11', d)} \ + jpeg ogg pango png theora vorbis \ + ${@bb.utils.contains('DISTRO_FEATURES', 'wayland', 'wayland egl', '', d)} \ +" + +OPENGL_APIS = 'opengl gles2' +OPENGL_PLATFORMS = 'egl glx' + +X11DEPENDS = "virtual/libx11 libsm libxrender libxv" +X11ENABLEOPTS = "-Dx11=enabled -Dxvideo=enabled -Dxshm=enabled" +X11DISABLEOPTS = "-Dx11=disabled -Dxvideo=disabled -Dxshm=disabled" + +PACKAGECONFIG[alsa] = "-Dalsa=enabled,-Dalsa=disabled,alsa-lib" +PACKAGECONFIG[cdparanoia] = "-Dcdparanoia=enabled,-Dcdparanoia=disabled,cdparanoia" +PACKAGECONFIG[graphene] = "-Dgl-graphene=enabled,-Dgl-graphene=disabled,graphene" +PACKAGECONFIG[jpeg] = "-Dgl-jpeg=enabled,-Dgl-jpeg=disabled,jpeg" +PACKAGECONFIG[ogg] = "-Dogg=enabled,-Dogg=disabled,libogg" +PACKAGECONFIG[opus] = "-Dopus=enabled,-Dopus=disabled,libopus" +PACKAGECONFIG[pango] = "-Dpango=enabled,-Dpango=disabled,pango" +PACKAGECONFIG[png] = "-Dgl-png=enabled,-Dgl-png=disabled,libpng" +# This enables Qt5 QML examples in -base. The Qt5 GStreamer +# qmlglsink and qmlglsrc plugins still exist in -good. +PACKAGECONFIG[qt5] = "-Dqt5=enabled,-Dqt5=disabled,qtbase qtdeclarative qtbase-native" +PACKAGECONFIG[theora] = "-Dtheora=enabled,-Dtheora=disabled,libtheora" +PACKAGECONFIG[tremor] = "-Dtremor=enabled,-Dtremor=disabled,tremor" +PACKAGECONFIG[visual] = "-Dlibvisual=enabled,-Dlibvisual=disabled,libvisual" +PACKAGECONFIG[vorbis] = "-Dvorbis=enabled,-Dvorbis=disabled,libvorbis" +PACKAGECONFIG[x11] = "${X11ENABLEOPTS},${X11DISABLEOPTS},${X11DEPENDS}" + +# OpenGL API packageconfigs +PACKAGECONFIG[opengl] = ",,virtual/libgl libglu" +PACKAGECONFIG[gles2] = ",,virtual/libgles2" + +# OpenGL platform packageconfigs +PACKAGECONFIG[egl] = ",,virtual/egl" +PACKAGECONFIG[glx] = ",,virtual/libgl" + +# OpenGL window systems (except for X11) +PACKAGECONFIG[gbm] = ",,virtual/libgbm libgudev libdrm" +PACKAGECONFIG[wayland] = ",,wayland-native wayland wayland-protocols libdrm" +PACKAGECONFIG[dispmanx] = ",,virtual/libomxil" +PACKAGECONFIG[viv-fb] = ",,virtual/libgles2 virtual/libg2d" + +OPENGL_WINSYS = "${@bb.utils.filter('PACKAGECONFIG', 'x11 gbm wayland dispmanx egl viv-fb', d)}" + +EXTRA_OEMESON += " \ + -Ddoc=disabled \ + ${@get_opengl_cmdline_list('gl_api', d.getVar('OPENGL_APIS'), d)} \ + ${@get_opengl_cmdline_list('gl_platform', d.getVar('OPENGL_PLATFORMS'), d)} \ + ${@get_opengl_cmdline_list('gl_winsys', d.getVar('OPENGL_WINSYS'), d)} \ +" + +FILES:${PN}-dev += "${libdir}/gstreamer-1.0/include/gst/gl/gstglconfig.h" +FILES:${MLPREFIX}libgsttag-1.0 += "${datadir}/gst-plugins-base/1.0/license-translations.dict" + +def get_opengl_cmdline_list(switch_name, options, d): + selected_options = [] + if bb.utils.contains('DISTRO_FEATURES', 'opengl', True, False, d): + for option in options.split(): + if bb.utils.contains('PACKAGECONFIG', option, True, False, d): + selected_options += [option] + if selected_options: + return '-D' + switch_name + '=' + ','.join(selected_options) + else: + return '' + +CVE_PRODUCT += "gst-plugins-base" + +########### End of OE-core copy ########### + +########### i.MX overrides ################ + +DEFAULT_PREFERENCE = "-1" + +LIC_FILES_CHKSUM = "file://LICENSE.txt;md5=69333daa044cb77e486cc36129f7a770" + +SRC_URI:remove = " \ + https://gstreamer.freedesktop.org/src/gst-plugins-base/gst-plugins-base-${PV}.tar.xz \ + file://0001-ENGR00312515-get-caps-from-src-pad-when-query-caps.patch \ + file://0003-viv-fb-Make-sure-config.h-is-included.patch \ + file://0002-ssaparse-enhance-SSA-text-lines-parsing.patch" +SRC_URI:prepend = "${GST1.0-PLUGINS-BASE_SRC};branch=${SRCBRANCH} " + +GST1.0-PLUGINS-BASE_SRC ?= "gitsm://github.com/nxp-imx/gst-plugins-base.git;protocol=https" +SRCBRANCH = "MM_04.09.02_2410_L6.6.y" +SRCREV = "031d262e65333e58b2ce01aaf47ab1012f93b21e" + +S = "${WORKDIR}/git" + +inherit use-imx-headers + +PACKAGECONFIG:remove = "${PACKAGECONFIG_REMOVE}" +PACKAGECONFIG_REMOVE ?= "jpeg" + +PACKAGECONFIG:append = " ${PACKAGECONFIG_G2D}" +PACKAGECONFIG_G2D ??= "" +PACKAGECONFIG_G2D:imxgpu2d ??= "g2d" + +PACKAGECONFIG[g2d] = ",,virtual/libg2d" +PACKAGECONFIG[viv-fb] = ",,virtual/libgles2" + +EXTRA_OEMESON += "-Dc_args="${CFLAGS} -I${STAGING_INCDIR_IMX}"" + +# links with imx-gpu libs which are pre-built for glibc +# gcompat will address it during runtime +LDFLAGS:append:imxgpu:libc-musl = " -Wl,--allow-shlib-undefined" + +COMPATIBLE_MACHINE = "(imx-nxp-bsp)" + +########### End of i.MX overrides ######### diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base_1.24.7.imx.bbappend b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base_1.24.7.imx.bbappend new file mode 100644 index 000000000..528d7e1a3 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-base_1.24.7.imx.bbappend @@ -0,0 +1,17 @@ +PACKAGECONFIG_GL:imxgpu2d = \ + "${@bb.utils.contains('DISTRO_FEATURES', 'opengl x11', 'opengl', '', d)}" +PACKAGECONFIG_GL:imxgpu3d = \ + "${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'gles2 egl', '', d)}" +PACKAGECONFIG_GL:use-mainline-bsp = \ + "${@bb.utils.contains('DISTRO_FEATURES', 'opengl', 'gles2 egl gbm', '', d)}" + +# The i.MX8 uses KMS instead of the Vivante specific framebuffer API. +# The i.MX7 does not have a GPU, except for ULP. +# This leaves the i.MX6 - with the vendor BSP - as the remaining use case for viv-fb. +# +# (Note that viv-fb is about the _windowing system_. Vivante direct texture support +# does not depend on the viv-fb feature. It used to, but that was actually a bug +# which was fixed in GStreamer 1.22.5. Since then, the direct texture support is +# detected by Meson by checking for direct texture symbols like "glTexDirectVIV".) +PACKAGECONFIG_GL:imxgpu2d:append:mx6-nxp-bsp = " viv-fb" +PACKAGECONFIG_GL:imxgpu2d:append:mx7-nxp-bsp = " viv-fb" diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-good_1.24.7.imx.bb b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-good_1.24.7.imx.bb new file mode 100644 index 000000000..1147c563a --- /dev/null +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0-plugins-good_1.24.7.imx.bb @@ -0,0 +1,121 @@ +# This recipe is for the i.MX fork of gstreamer1.0-plugins-good. For ease of +# maintenance, the top section is a verbatim copy of an OE-core +# recipe. The second section customizes the recipe for i.MX. + +########### OE-core copy ################## +# Upstream hash: 937817e5164f8af8452aec03ae3c45cb23d63df9 + +require recipes-multimedia/gstreamer/gstreamer1.0-plugins-common.inc + +SUMMARY = "'Good' GStreamer plugins" +HOMEPAGE = "https://gstreamer.freedesktop.org/" +BUGTRACKER = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/issues" + +SRC_URI = "https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-${PV}.tar.xz \ + file://0001-qt-include-ext-qt-gstqtgl.h-instead-of-gst-gl-gstglf.patch \ + file://0001-v4l2-Define-ioctl_req_t-for-posix-linux-case.patch" + +SRC_URI[sha256sum] = "b67b31313a54c6929b82969d41d3cfdf2f58db573fb5f491e6bba5d84aea0778" + +S = "${WORKDIR}/gst-plugins-good-${PV}" + +LICENSE = "LGPL-2.1-or-later" +LIC_FILES_CHKSUM = "file://COPYING;md5=a6f89e2100d9b6cdffcea4f398e37343 \ + file://gst/replaygain/rganalysis.c;beginline=1;endline=23;md5=b60ebefd5b2f5a8e0cab6bfee391a5fe" + +DEPENDS += "gstreamer1.0-plugins-base libcap zlib" +RPROVIDES:${PN}-pulseaudio += "${PN}-pulse" +RPROVIDES:${PN}-soup += "${PN}-souphttpsrc" +RDEPENDS:${PN}-soup += "${MLPREFIX}${@bb.utils.contains('PACKAGECONFIG', 'soup2', 'libsoup-2.4', 'libsoup', d)}" + +PACKAGECONFIG_SOUP ?= "soup3" + +PACKAGECONFIG ??= " \ + ${GSTREAMER_ORC} \ + ${PACKAGECONFIG_SOUP} \ + ${@bb.utils.filter('DISTRO_FEATURES', 'pulseaudio x11', d)} \ + ${@bb.utils.contains('TUNE_FEATURES', 'm64', 'asm', '', d)} \ + bz2 cairo flac gdk-pixbuf gudev jpeg lame libpng mpg123 speex taglib v4l2 \ +" + +X11DEPENDS = "virtual/libx11 libsm libxrender libxfixes libxdamage" +X11ENABLEOPTS = "-Dximagesrc=enabled -Dximagesrc-xshm=enabled -Dximagesrc-xfixes=enabled -Dximagesrc-xdamage=enabled" +X11DISABLEOPTS = "-Dximagesrc=disabled -Dximagesrc-xshm=disabled -Dximagesrc-xfixes=disabled -Dximagesrc-xdamage=disabled" + +QT5WAYLANDDEPENDS = "${@bb.utils.contains("DISTRO_FEATURES", "wayland", "qtwayland", "", d)}" + +PACKAGECONFIG[asm] = "-Dasm=enabled,-Dasm=disabled,nasm-native" +PACKAGECONFIG[bz2] = "-Dbz2=enabled,-Dbz2=disabled,bzip2" +PACKAGECONFIG[cairo] = "-Dcairo=enabled,-Dcairo=disabled,cairo" +PACKAGECONFIG[dv1394] = "-Ddv1394=enabled,-Ddv1394=disabled,libiec61883 libavc1394 libraw1394" +PACKAGECONFIG[flac] = "-Dflac=enabled,-Dflac=disabled,flac" +PACKAGECONFIG[gdk-pixbuf] = "-Dgdk-pixbuf=enabled,-Dgdk-pixbuf=disabled,gdk-pixbuf" +PACKAGECONFIG[gtk] = "-Dgtk3=enabled,-Dgtk3=disabled,gtk+3" +PACKAGECONFIG[gudev] = "-Dv4l2-gudev=enabled,-Dv4l2-gudev=disabled,libgudev" +PACKAGECONFIG[jack] = "-Djack=enabled,-Djack=disabled,jack" +PACKAGECONFIG[jpeg] = "-Djpeg=enabled,-Djpeg=disabled,jpeg" +PACKAGECONFIG[lame] = "-Dlame=enabled,-Dlame=disabled,lame" +PACKAGECONFIG[libpng] = "-Dpng=enabled,-Dpng=disabled,libpng" +PACKAGECONFIG[libv4l2] = "-Dv4l2-libv4l2=enabled,-Dv4l2-libv4l2=disabled,v4l-utils" +PACKAGECONFIG[mpg123] = "-Dmpg123=enabled,-Dmpg123=disabled,mpg123" +PACKAGECONFIG[pulseaudio] = "-Dpulse=enabled,-Dpulse=disabled,pulseaudio" +PACKAGECONFIG[qt5] = "-Dqt5=enabled,-Dqt5=disabled,qtbase qtdeclarative qtbase-native ${QT5WAYLANDDEPENDS}" +PACKAGECONFIG[soup2] = "-Dsoup=enabled,,libsoup-2.4,,,soup3" +PACKAGECONFIG[soup3] = "-Dsoup=enabled,,libsoup,,,soup2" +PACKAGECONFIG[speex] = "-Dspeex=enabled,-Dspeex=disabled,speex" +PACKAGECONFIG[rpi] = "-Drpicamsrc=enabled,-Drpicamsrc=disabled,userland" +PACKAGECONFIG[taglib] = "-Dtaglib=enabled,-Dtaglib=disabled,taglib" +PACKAGECONFIG[v4l2] = "-Dv4l2=enabled -Dv4l2-probe=true,-Dv4l2=disabled -Dv4l2-probe=false" +PACKAGECONFIG[vpx] = "-Dvpx=enabled,-Dvpx=disabled,libvpx" +PACKAGECONFIG[wavpack] = "-Dwavpack=enabled,-Dwavpack=disabled,wavpack" +PACKAGECONFIG[x11] = "${X11ENABLEOPTS},${X11DISABLEOPTS},${X11DEPENDS}" + +EXTRA_OEMESON += " \ + -Ddoc=disabled \ + -Daalib=disabled \ + -Ddirectsound=disabled \ + -Ddv=disabled \ + -Dlibcaca=disabled \ + -Doss=enabled \ + -Doss4=disabled \ + -Dosxaudio=disabled \ + -Dosxvideo=disabled \ + -Dshout2=disabled \ + -Dtwolame=disabled \ + -Dwaveform=disabled \ +" + +FILES:${PN}-equalizer += "${datadir}/gstreamer-1.0/presets/*.prs" + +########### End of OE-core copy ########### + +########### i.MX overrides ################ + +DEFAULT_PREFERENCE = "-1" + +LIC_FILES_CHKSUM = " \ + file://LICENSE.txt;md5=69333daa044cb77e486cc36129f7a770 \ + file://gst/replaygain/rganalysis.c;beginline=1;endline=23;md5=b60ebefd5b2f5a8e0cab6bfee391a5fe \ +" +# Enable pulsesink in gstreamer +PACKAGECONFIG:append = " pulseaudio" + +# fb implementation of v4l2 uses libdrm +DEPENDS += "${@bb.utils.contains('PACKAGECONFIG', 'v4l2', '${DEPENDS_V4L2}', '', d)}" +DEPENDS_V4L2 = "${@bb.utils.contains_any('DISTRO_FEATURES', 'wayland x11', '', 'libdrm', d)}" + +SRC_URI:remove = "https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-${PV}.tar.xz \ + file://0001-qt-include-ext-qt-gstqtgl.h-instead-of-gst-gl-gstglf.patch \ + file://0001-v4l2-Define-ioctl_req_t-for-posix-linux-case.patch \ +" + +SRC_URI:prepend = "${GST1.0-PLUGINS-GOOD_SRC};branch=${SRCBRANCH} " +GST1.0-PLUGINS-GOOD_SRC ?= "gitsm://github.com/nxp-imx/gst-plugins-good.git;protocol=https" +SRCBRANCH = "MM_04.09.02_2410_L6.6.y" +SRCREV = "33b2732b873758a302064818b35dcad41b5bbc14" + +S = "${WORKDIR}/git" + +COMPATIBLE_MACHINE = "(imx-nxp-bsp)" + +########### End of i.MX overrides ######### diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0/run-ptest b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0/run-ptest new file mode 100755 index 000000000..0cfa955f0 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0/run-ptest @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +gnome-desktop-testing-runner gstreamer diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0_1.24.7.imx.bb b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0_1.24.7.imx.bb new file mode 100644 index 000000000..a17c20f42 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/gstreamer1.0_1.24.7.imx.bb @@ -0,0 +1,112 @@ +# This recipe is for the i.MX fork of gstreamer1.0. For ease of +# maintenance, the top section is a verbatim copy of an OE-core +# recipe. The second section customizes the recipe for i.MX. + +########### OE-core copy ################## +# Upstream hash: 937817e5164f8af8452aec03ae3c45cb23d63df9 + +SUMMARY = "GStreamer 1.0 multimedia framework" +DESCRIPTION = "GStreamer is a multimedia framework for encoding and decoding video and sound. \ +It supports a wide range of formats including mp3, ogg, avi, mpeg and quicktime." +HOMEPAGE = "http://gstreamer.freedesktop.org/" +BUGTRACKER = "https://bugzilla.gnome.org/enter_bug.cgi?product=Gstreamer" +SECTION = "multimedia" +LICENSE = "LGPL-2.1-or-later" + +DEPENDS = "glib-2.0 glib-2.0-native libxml2 bison-native flex-native" + +inherit meson pkgconfig gettext upstream-version-is-even gobject-introspection ptest-gnome + +LIC_FILES_CHKSUM = "file://COPYING;md5=69333daa044cb77e486cc36129f7a770 \ + file://gst/gst.h;beginline=1;endline=21;md5=e059138481205ee2c6fc1c079c016d0d" + +S = "${WORKDIR}/gstreamer-${PV}" + +SRC_URI = "https://gstreamer.freedesktop.org/src/gstreamer/gstreamer-${PV}.tar.xz \ + file://run-ptest \ + file://0001-tests-respect-the-idententaion-used-in-meson.patch \ + file://0002-tests-add-support-for-install-the-tests.patch \ + file://0003-tests-use-a-dictionaries-for-environment.patch;striplevel=3 \ + file://0004-tests-add-helper-script-to-run-the-installed_tests.patch;striplevel=3 \ + " +SRC_URI[sha256sum] = "4408d7930f381809e85917acc19712f173261ba85bdf20c5567b2a21b1193b61" + +PACKAGECONFIG ??= "${@bb.utils.contains('PTEST_ENABLED', '1', 'tests', '', d)} \ + check \ + debug \ + tools" + +PACKAGECONFIG[debug] = "-Dgst_debug=true,-Dgst_debug=false" +PACKAGECONFIG[tracer-hooks] = "-Dtracer_hooks=true,-Dtracer_hooks=false" +PACKAGECONFIG[coretracers] = "-Dcoretracers=enabled,-Dcoretracers=disabled" +PACKAGECONFIG[check] = "-Dcheck=enabled,-Dcheck=disabled" +PACKAGECONFIG[tests] = "-Dtests=enabled -Dinstalled_tests=true,-Dtests=disabled -Dinstalled_tests=false" +PACKAGECONFIG[unwind] = "-Dlibunwind=enabled,-Dlibunwind=disabled,libunwind" +PACKAGECONFIG[dw] = "-Dlibdw=enabled,-Dlibdw=disabled,elfutils" +PACKAGECONFIG[bash-completion] = "-Dbash-completion=enabled,-Dbash-completion=disabled,bash-completion" +PACKAGECONFIG[tools] = "-Dtools=enabled,-Dtools=disabled" +PACKAGECONFIG[setcap] = "-Dptp-helper-permissions=capabilities,,libcap libcap-native" + +# TODO: put this in a gettext.bbclass patch +def gettext_oemeson(d): + if d.getVar('USE_NLS') == 'no': + return '-Dnls=disabled' + # Remove the NLS bits if USE_NLS is no or INHIBIT_DEFAULT_DEPS is set + if d.getVar('INHIBIT_DEFAULT_DEPS') and not oe.utils.inherits(d, 'cross-canadian'): + return '-Dnls=disabled' + return '-Dnls=enabled' + +EXTRA_OEMESON += " \ + -Ddoc=disabled \ + -Dexamples=disabled \ + -Ddbghelp=disabled \ + ${@gettext_oemeson(d)} \ +" + +GIR_MESON_ENABLE_FLAG = "enabled" +GIR_MESON_DISABLE_FLAG = "disabled" + +PACKAGES += "${PN}-bash-completion" + +# Add the core element plugins to the main package +FILES:${PN} += "${libdir}/gstreamer-1.0/*.so" +FILES:${PN}-dev += "${libdir}/gstreamer-1.0/*.a ${libdir}/gstreamer-1.0/include" +FILES:${PN}-bash-completion += "${datadir}/bash-completion/completions/ ${datadir}/bash-completion/helpers/gst*" +FILES:${PN}-dbg += "${datadir}/gdb ${datadir}/gstreamer-1.0/gdb" + +RDEPENDS:${PN}-ptest:append:libc-glibc = " glibc-gconv-iso8859-5" + +CVE_PRODUCT = "gstreamer" + +PTEST_BUILD_HOST_FILES = "" + +########### End of OE-core copy ########### + +########### i.MX overrides ################ + +DEFAULT_PREFERENCE = "-1" + +LIC_FILES_CHKSUM = " \ + file://LICENSE.txt;md5=69333daa044cb77e486cc36129f7a770 \ + file://gst/gst.h;beginline=1;endline=21;md5=e059138481205ee2c6fc1c079c016d0d \ +" + +# Use i.MX fork of GST for customizations +SRC_URI:remove = "https://gstreamer.freedesktop.org/src/gstreamer/gstreamer-${PV}.tar.xz \ + file://0001-tests-respect-the-idententaion-used-in-meson.patch \ + file://0002-tests-add-support-for-install-the-tests.patch \ + file://0003-tests-use-a-dictionaries-for-environment.patch;striplevel=3 \ + file://0004-tests-add-helper-script-to-run-the-installed_tests.patch;striplevel=3 \ +" +SRC_URI:prepend = "${GST1.0_SRC};branch=${SRCBRANCH} " +GST1.0_SRC ?= "gitsm://github.com/nxp-imx/gstreamer.git;protocol=https" +SRCBRANCH = "MM_04.09.02_2410_L6.6.y" +SRCREV = "1a8f23da16cd224574ab2386803149c76f2cb628" + +S = "${WORKDIR}/git" + +PACKAGECONFIG[tests] = "-Dtests=enabled,-Dtests=disabled" + +COMPATIBLE_MACHINE = "(imx-nxp-bsp)" + +########### End of i.MX overrides ######### diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/imx-gst1.0-plugin/0002-imx-gst1.0-plugin-fix-build-using-MUSL-C-library.patch b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/imx-gst1.0-plugin/0002-imx-gst1.0-plugin-fix-build-using-MUSL-C-library.patch deleted file mode 100644 index 97d85f17b..000000000 --- a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/imx-gst1.0-plugin/0002-imx-gst1.0-plugin-fix-build-using-MUSL-C-library.patch +++ /dev/null @@ -1,36 +0,0 @@ -From: Javier Viguera -Date: Thu, 2 Jan 2020 16:49:51 +0100 -Subject: [PATCH] imx-gst1.0-plugin: fix build using MUSL C library - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Javier Viguera ---- - tools/gplay2/gplay2.c | 1 - - tools/grecorder/grecorder.c | 1 + - 2 files changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tools/gplay2/gplay2.c b/tools/gplay2/gplay2.c -index 9e406b9..ae193b7 100755 ---- a/tools/gplay2/gplay2.c -+++ b/tools/gplay2/gplay2.c -@@ -26,7 +26,6 @@ - */ - - --#include - #include - #include - #include -diff --git a/tools/grecorder/grecorder.c b/tools/grecorder/grecorder.c -index c315605..ee772b0 100644 ---- a/tools/grecorder/grecorder.c -+++ b/tools/grecorder/grecorder.c -@@ -27,6 +27,7 @@ - #include - #include - #define __USE_LARGEFILE64 -+#define _GNU_SOURCE # For MUSL statvfs64 - #include - - #include "recorder_engine.h" diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/imx-gst1.0-plugin_%.bbappend b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/imx-gst1.0-plugin_%.bbappend index 8e9877a34..7d047b0a5 100644 --- a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/imx-gst1.0-plugin_%.bbappend +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/imx-gst1.0-plugin_%.bbappend @@ -1,8 +1,12 @@ -# Copyright (C) 2016-2024, Digi International Inc. +# Copyright (C) 2016-2025, Digi International Inc. FILESEXTRAPATHS:prepend := "${THISDIR}/${BPN}:" +PV = "4.9.2+git${SRCPV}" + SRC_URI:append:ccimx6ul = " \ file://0001-gstimxv4l2-map-dev-video1-to-dev-fb0.patch \ - file://0002-imx-gst1.0-plugin-fix-build-using-MUSL-C-library.patch \ " + +SRCBRANCH = "MM_04.09.02_2410_L6.6.y" +SRCREV = "ef9c1a080e739e6f0be878148d9f4a050dc83bec" From fb655dd021c6ccb38211a1e019d03290be1f5e80 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Tue, 6 May 2025 15:24:51 +0200 Subject: [PATCH 083/138] meta-digi: xwayland: use poky's xwayland 23.2.5 recipe That includes several CVE patches not present of meta-freescale's 23.2.5.imx recipe (which is based in exactly the same revision). Similar change was done in NXP's meta-imx (see commit 99ceb057fcfdc8151c1488089d5f22363dfdb6d7). Signed-off-by: Javier Viguera --- .../conf/machine/include/imx-digi-base.inc | 2 -- ...-create-GLES2-context-for-glamor-EGL.patch | 31 +++++++++++++++++++ .../xwayland/xwayland_23.2.5.bbappend | 18 +++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/xwayland/xwayland/0001-Prefer-to-create-GLES2-context-for-glamor-EGL.patch create mode 100644 meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/xwayland/xwayland_23.2.5.bbappend diff --git a/meta-digi-arm/conf/machine/include/imx-digi-base.inc b/meta-digi-arm/conf/machine/include/imx-digi-base.inc index 2824b8fa2..5c815ef9f 100644 --- a/meta-digi-arm/conf/machine/include/imx-digi-base.inc +++ b/meta-digi-arm/conf/machine/include/imx-digi-base.inc @@ -297,8 +297,6 @@ PREFERRED_VERSION_weston:imx-mainline-bsp = "" PREFERRED_VERSION_wayland-protocols:imx-nxp-bsp ??= "1.32.imx" -PREFERRED_VERSION_xwayland:imx-nxp-bsp ??= "23.2.5.imx" - # Use i.MX libdrm Version PREFERRED_VERSION_libdrm:imx-nxp-bsp ??= "2.4.116.imx" diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/xwayland/xwayland/0001-Prefer-to-create-GLES2-context-for-glamor-EGL.patch b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/xwayland/xwayland/0001-Prefer-to-create-GLES2-context-for-glamor-EGL.patch new file mode 100644 index 000000000..c1e8ae11a --- /dev/null +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/xwayland/xwayland/0001-Prefer-to-create-GLES2-context-for-glamor-EGL.patch @@ -0,0 +1,31 @@ +From e75a4d7b4474529967490de6e75ae4f1b7128937 Mon Sep 17 00:00:00 2001 +From: Xianzhong +Date: Sat, 22 Jan 2022 17:57:59 +0800 +Subject: [PATCH 1/3] Prefer to create GLES2 context for glamor EGL + +created the initial patch for xwayland-21.1.2 + +Upstream-Status: Inappropriate [embedded specific] +Signed-off-by: Xianzhong +--- + hw/xwayland/xwayland-glamor-gbm.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/hw/xwayland/xwayland-glamor-gbm.c b/hw/xwayland/xwayland-glamor-gbm.c +index 12d820e44..8a89919be 100644 +--- a/hw/xwayland/xwayland-glamor-gbm.c ++++ b/hw/xwayland/xwayland-glamor-gbm.c +@@ -866,8 +866,8 @@ xwl_glamor_gbm_init_egl(struct xwl_screen *xwl_screen) + goto error; + } + +- if (!xwl_glamor_try_big_gl_api(xwl_screen) && +- !xwl_glamor_try_gles_api(xwl_screen)) { ++ if (!xwl_glamor_try_gles_api(xwl_screen) && ++ !xwl_glamor_try_big_gl_api(xwl_screen)) { + ErrorF("Cannot use neither GL nor GLES2\n"); + goto error; + } +-- +2.17.1 + diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/xwayland/xwayland_23.2.5.bbappend b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/xwayland/xwayland_23.2.5.bbappend new file mode 100644 index 000000000..d8f5bdde5 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/xwayland/xwayland_23.2.5.bbappend @@ -0,0 +1,18 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +SRC_URI:append:imxgpu = " \ + file://0001-Prefer-to-create-GLES2-context-for-glamor-EGL.patch \ +" + +OPENGL_PKGCONFIGS:remove:imxgpu = "${OPENGL_PKGCONFIGS_REMOVE_IMXGPU}" +OPENGL_PKGCONFIGS_REMOVE_IMXGPU = "" +OPENGL_PKGCONFIGS_REMOVE_IMXGPU:imx-nxp-bsp = "glamor glx" +OPENGL_PKGCONFIGS_REMOVE_IMXGPU:mx8-nxp-bsp = "glx" + +# links with imx-gpu libs which are pre-built for glibc +# gcompat will address it during runtime +LDFLAGS:append:imxgpu:libc-musl = " -Wl,--allow-shlib-undefined" + +RDEPENDS:${PN}:append:imxgpu:libc-musl = " gcompat" + +PACKAGE_ARCH:imxgpu = "${MACHINE_SOCARCH}" From 554ab9a536ee67124e5a57a50a20422d6f03e2fe Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Tue, 6 May 2025 15:25:22 +0200 Subject: [PATCH 084/138] firmware-ele-imx: update firmware to match NXP rel_imx_6.6.52_2.2.0 Signed-off-by: Javier Viguera --- .../recipes-bsp/firmware-imx/firmware-ele-imx_%.bbappend | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/firmware-imx/firmware-ele-imx_%.bbappend b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/firmware-imx/firmware-ele-imx_%.bbappend index 628c46422..78bbf712f 100644 --- a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/firmware-imx/firmware-ele-imx_%.bbappend +++ b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/firmware-imx/firmware-ele-imx_%.bbappend @@ -3,6 +3,10 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/${BPN}:" # Forward-port the i.MX93 A0 fw from v0.1.0 SRC_URI:append:ccimx93 = " file://mx93a0-ahab-container.img" + +IMX_SRCREV_ABBREV = "7b1e150" +SRC_URI[sha256sum] = "09165fe5df75ad665df304e89d494a02c5f379624604d230e1b595cb5ae3b5b8" + UNPACK_POSTFUNC = "" UNPACK_POSTFUNC:ccimx93 = "copy_ele_a0_fw" copy_ele_a0_fw() { From a8c3bba9a85e155b23fc037cc3478ccc694446b0 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Thu, 8 May 2025 11:53:44 +0200 Subject: [PATCH 085/138] firmware-murata: update firmware binaries to version imx-scarthgap-jaculus_r1.1 This commit updates the firmware binaries for Bluetooth and Wireless interfaces, aligned with the Cypress Linux WiFi Driver (FMAC) release v6.1.97-2025_0219. The updated wireless firmware versions are as follows: - 2FY Wireless chip: v28.10.387.16 - 2AE Wireless chip: v13.10.246.356 These updates are included as part of the imx-scarthgap-jaculus_r1.1 Murata release. Signed-off-by: Arturo Buzarra --- .../firmware-murata-infineon.bb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/meta-digi-arm/recipes-bsp/firmware-murata-infineon/firmware-murata-infineon.bb b/meta-digi-arm/recipes-bsp/firmware-murata-infineon/firmware-murata-infineon.bb index bf0538a18..39582ebc1 100644 --- a/meta-digi-arm/recipes-bsp/firmware-murata-infineon/firmware-murata-infineon.bb +++ b/meta-digi-arm/recipes-bsp/firmware-murata-infineon/firmware-murata-infineon.bb @@ -6,7 +6,7 @@ LICENSE = "CYPRESS-EULA" LIC_FILES_CHKSUM = "file://${S}/cyw-bt-patch/LICENCE.cypress;md5=cbc5f665d04f741f1e006d2096236ba7" SRC_URI = " \ - https://github.com/Infineon/ifx-linux-firmware/archive/refs/tags/release-v6.1.97-2024_1115.tar.gz;destsuffix=cyw-fmac-fw-ifx;name=cyw-fmac-fw-ifx \ + https://github.com/Infineon/ifx-linux-firmware/archive/refs/tags/release-v6.1.97-2025_0219.tar.gz;destsuffix=cyw-fmac-fw-ifx;name=cyw-fmac-fw-ifx \ git://github.com/murata-wireless/cyw-fmac-fw;protocol=http;branch=jaculus;destsuffix=cyw-fmac-fw;name=cyw-fmac-fw \ git://github.com/murata-wireless/cyw-fmac-nvram;protocol=http;branch=jaculus;destsuffix=cyw-fmac-nvram;name=cyw-fmac-nvram \ git://github.com/murata-wireless/cyw-bt-patch;protocol=http;branch=master;destsuffix=cyw-bt-patch;name=cyw-bt-patch \ @@ -26,10 +26,10 @@ SRC_URI:append:ccmp2 = " \ file://mbt \ " -SRC_URI[cyw-fmac-fw-ifx.sha256sum]="34f5bfac6476d849af26f945705dc5a19965825333840405ef25dcd835d521d7" +SRC_URI[cyw-fmac-fw-ifx.sha256sum]="1ff021a1b3ef1608f5c0ad4b39fec8e0f5cb8c204d178f1de0f8215d06abc8d9" SRCREV_cyw-fmac-fw="a5cb86a5d11192ba6e7738f82b4d2dc9eeeca679" SRCREV_cyw-fmac-nvram="146d1438372b6c4857f92b8769b91c1801d3ede2" -SRCREV_cyw-bt-patch="742f5cec14c98000dc532518c9e5acaec40de668" +SRCREV_cyw-bt-patch="23de75a4e5384d16e8478f668b769b0d24ede0de" SRCREV_cyw-fmac-utils-imx32="dad9ed86bf6691910197bc91d42a45ea8175180c" SRCREV_cyw-fmac-utils-imx64="368bd9a4163e115468d79c238192b41f6266c523" @@ -78,7 +78,7 @@ do_install:append:ccmp1 () { # Install WLAN firmware file (*.bin) and Regulatory binary file (*.clm_blob) # For Murata 2AE (LBEE5PK2AE-564) - install -m 444 ${S}/ifx-linux-firmware-release-v6.1.97-2024_1115/firmware/cyfmac4373-sdio.industrial.bin ${D}${base_libdir}/firmware/cypress/cyfmac4373-sdio.bin + install -m 444 ${S}/ifx-linux-firmware-release-v6.1.97-2025_0219/firmware/cyfmac4373-sdio.industrial.bin ${D}${base_libdir}/firmware/cypress/cyfmac4373-sdio.bin install -m 444 cyfmac4373-sdio_US.clm_blob ${D}${base_libdir}/firmware/cypress/cyfmac4373-sdio_US.clm_blob install -m 444 cyfmac4373-sdio_World.clm_blob ${D}${base_libdir}/firmware/cypress/cyfmac4373-sdio_World.clm_blob @@ -97,7 +97,7 @@ do_install:append:ccmp2 () { # Install WLAN firmware file (*.bin) and Regulatory binary file (*.clm_blob) # For Murata 2GY (LBEE5HY2GY) and Murata 2FY (LBEE5HY2FY) - install -m 444 ${S}/ifx-linux-firmware-release-v6.1.97-2024_1115/firmware/cyfmac55500-sdio.trxse ${D}${base_libdir}/firmware/cypress/cyfmac55500-sdio.trxse + install -m 444 ${S}/ifx-linux-firmware-release-v6.1.97-2025_0219/firmware/cyfmac55500-sdio.trxse ${D}${base_libdir}/firmware/cypress/cyfmac55500-sdio.trxse install -m 444 ${S}/cyw-fmac-fw/cyfmac55500-sdio.2FY.clm_blob ${D}${base_libdir}/firmware/cypress/cyfmac55500-sdio_US.clm_blob # Install NVRAM files (*.txt) From 1ae68ffa59554629cbf80f31a95d06535c6954e6 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Thu, 8 May 2025 14:47:57 +0200 Subject: [PATCH 086/138] meta-digi-dey: update wayland for release rel_imx_6.6.52_2.2.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove wayland_1.22.0.bbappend, with changes originally added for Crank UI (commit 25c699e05b4b), which we no longer support. * Update wayland_%.bbappend to align with NXP’s rel_imx_6.6.52_2.2.0 release. Signed-off-by: Javier Viguera --- .../recipes-graphics/wayland/wayland_%.bbappend | 12 +++++++++--- .../wayland/wayland_1.22.0.bbappend | 13 ------------- 2 files changed, 9 insertions(+), 16 deletions(-) delete mode 100644 meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/wayland/wayland_1.22.0.bbappend diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/wayland/wayland_%.bbappend b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/wayland/wayland_%.bbappend index e032e878e..e7f32d698 100644 --- a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/wayland/wayland_%.bbappend +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/wayland/wayland_%.bbappend @@ -1,6 +1,9 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" -SRC_URI:append:imxgpu:arm = " \ +# Add patches specific to i.MX 6 and 7 with GPU and set package arch accordingly +SRC_URI += "${SRC_URI_IMXGPU_PATCHES}" +SRC_URI_IMXGPU_PATCHES = "" +SRC_URI_IMXGPU_PATCHES:imxgpu:mx6-nxp-bsp = " \ file://0101-Revert-client-Do-not-warn-about-attached-proxies-on-.patch \ file://0102-Revert-client-Abort-when-trying-to-add-an-event-to-a.patch \ file://0103-Revert-client-Warn-when-a-queue-is-destroyed-with-at.patch \ @@ -9,5 +12,8 @@ SRC_URI:append:imxgpu:arm = " \ file://0202-Set-the-default-max-buffer-size-to-unbounded.patch \ file://0203-Log-the-object-and-methods-when-marshalling-or-sendi.patch \ file://0204-Fix-buffer-resize-crash.patch \ - file://0205-connection-avoid-calling-memcpy-on-NULL-0.patch \ -" + file://0205-connection-avoid-calling-memcpy-on-NULL-0.patch" +SRC_URI_IMXGPU_PATCHES:imxgpu:mx7-nxp-bsp = " \ + ${SRC_URI_IMXGPU_PATCHES:imxgpu:mx6-nxp-bsp}" +PACKAGE_ARCH:imxgpu:mx6-nxp-bsp = "${MACHINE_SOCARCH}" +PACKAGE_ARCH:imxgpu:mx7-nxp-bsp = "${MACHINE_SOCARCH}" diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/wayland/wayland_1.22.0.bbappend b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/wayland/wayland_1.22.0.bbappend deleted file mode 100644 index 0e40d655e..000000000 --- a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/wayland/wayland_1.22.0.bbappend +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (C) 2020-2024, Digi International Inc. - -FILES_SOLIBSDEV = " \ - ${base_libdir}/lib*${SOLIBSDEV} \ - ${libdir}/libwayland-client.so \ - ${libdir}/libwayland-cursor.so \ - ${libdir}/libwayland-server.so \ -" -FILES:${PN} += "${libdir}/libwayland-egl.so" - -RPROVIDES:${PN} = "libwayland-egl.so()(64bit)" - -INSANE_SKIP:${PN} += "dev-so" From c2632d1b0078ccadf52ad2f981b7291b2a290484 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Thu, 8 May 2025 14:48:27 +0200 Subject: [PATCH 087/138] meta-digi-dey: update GPU-related packages to rel_imx_6.6.52_2.2.0 * Add imx-gpu-sdk 6.4.2 recipe * Update GPU tools package groups to match NXP release Signed-off-by: Javier Viguera --- ...ckagegroup-fsl-tools-gpu-external.bbappend | 8 + .../packagegroup-fsl-tools-gpu.bbappend | 8 + .../imx-gpu-sdk/imx-gpu-sdk_6.4.2.bb | 182 ++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 meta-digi-dey/dynamic-layers/freescale-layer/recipes-fsl/packagegroups/packagegroup-fsl-tools-gpu-external.bbappend create mode 100644 meta-digi-dey/dynamic-layers/freescale-layer/recipes-fsl/packagegroups/packagegroup-fsl-tools-gpu.bbappend create mode 100644 meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/imx-gpu-sdk/imx-gpu-sdk_6.4.2.bb diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-fsl/packagegroups/packagegroup-fsl-tools-gpu-external.bbappend b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-fsl/packagegroups/packagegroup-fsl-tools-gpu-external.bbappend new file mode 100644 index 000000000..bd1ce126f --- /dev/null +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-fsl/packagegroups/packagegroup-fsl-tools-gpu-external.bbappend @@ -0,0 +1,8 @@ +# Copyright (C) 2025, Digi International Inc. + +SOC_TOOLS_GPU_XWAYLAND:imxgpu3d = "glmark2 mesa-demos gtkperf" + +DRM_TOOLS = "" +DRM_TOOLS:imxdrm = "kmscube" + +RDEPENDS:${PN}:append:imxgpu = " ${DRM_TOOLS}" diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-fsl/packagegroups/packagegroup-fsl-tools-gpu.bbappend b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-fsl/packagegroups/packagegroup-fsl-tools-gpu.bbappend new file mode 100644 index 000000000..ae7116500 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-fsl/packagegroups/packagegroup-fsl-tools-gpu.bbappend @@ -0,0 +1,8 @@ +# Copyright (C) 2025, Digi International Inc. + +SOC_TOOLS_DRM ??= "" +SOC_TOOLS_DRM:imxdrm ??= "libdrm-tests" + +SOC_TOOLS_GPU:append:imxgpu = " gputop" + +RDEPENDS:${PN}:append = " ${SOC_TOOLS_DRM}" diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/imx-gpu-sdk/imx-gpu-sdk_6.4.2.bb b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/imx-gpu-sdk/imx-gpu-sdk_6.4.2.bb new file mode 100644 index 000000000..81641e992 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-graphics/imx-gpu-sdk/imx-gpu-sdk_6.4.2.bb @@ -0,0 +1,182 @@ +SUMMARY = "i.MX GPU SDK Samples" +DESCRIPTION = "Set of sample applications for i.MX GPU" +LICENSE = "BSD-3-Clause" +LIC_FILES_CHKSUM = "file://License.md;md5=f989c2fba947eb886cd6cb09cee89518" + +DEPENDS = " \ + assimp \ + cmake-native \ + devil \ + fmt \ + gli \ + glm \ + gstreamer1.0 \ + gstreamer1.0-plugins-base \ + gtest \ + half \ + ninja-native \ + nlohmann-json \ + pugixml \ + rapidjson \ + stb \ + zlib \ + ${DEPENDS_2D} \ + ${DEPENDS_3D} \ + ${DEPENDS_BACKEND} \ + ${DEPENDS_DRM} \ +" +DEPENDS_2D = "" +DEPENDS_2D:imxgpu2d = " \ + virtual/libg2d \ + virtual/libopenvg \ +" +DEPENDS_2D:mx95-nxp-bsp = " \ + virtual/libg2d \ +" +DEPENDS_3D = "" +DEPENDS_3D:imxgpu3d = " \ + virtual/libgles2 \ +" +DEPENDS_BACKEND = " \ + ${@bb.utils.contains('DISTRO_FEATURES', 'wayland', ' wayland-native wayland wayland-protocols', \ + bb.utils.contains('DISTRO_FEATURES', 'x11', ' xrandr', \ + '', d), d)} \ +" +DEPENDS_DRM = "" +DEPENDS_DRM:imxdrm = " \ + glslang-native \ + opencv \ + rapidopencl \ + rapidopenvx \ + rapidvulkan \ + vulkan-headers \ + vulkan-loader \ +" +DEPENDS_DRM:mx8mm-nxp-bsp = " \ + opencv \ +" + +SRC_URI = "${GPU_SDK_SRC};branch=${SRCBRANCH}" +GPU_SDK_SRC ?= "git://github.com/nxp-imx/gtec-demo-framework.git;protocol=https" +SRCBRANCH = "master" +SRCREV = "5e87170112c22b92ead46740b1e7caa5cd71c484" + +S = "${WORKDIR}/git" + +inherit pkgconfig + +WINDOW_SYSTEM = \ + "${@bb.utils.contains('DISTRO_FEATURES', 'wayland', 'Wayland', \ + bb.utils.contains('DISTRO_FEATURES', 'x11', 'X11', \ + 'FB', d), d)}" + +FEATURES = "ConsoleHost,EarlyAccess,EGL,GoogleUnitTest,Lib_NlohmannJson,Lib_pugixml,Test_RequireUserInputToExit,WindowHost" +FEATURES:append = "${FEATURES_GPU}${FEATURES_G2D}${FEATURES_3D}${FEATURES_SOC}" + +FEATURES_GPU = ",HW_GPU_VIVANTE,OpenVG" +FEATURES_GPU:mx95-nxp-bsp = "" + +FEATURES_G2D = "" +FEATURES_G2D:imxgpu2d = ",G2D" + +FEATURES_3D = "" +FEATURES_3D:imxgpu3d = ",OpenGLES2" + +FEATURES_SOC = "" +FEATURES_SOC:mx6q-nxp-bsp = ",OpenGLES3" +FEATURES_SOC:mx6dl-nxp-bsp = ",OpenGLES3" +FEATURES_SOC:mx8-nxp-bsp = ",OpenCV4,Vulkan1.2,OpenGLES3.2,OpenCL1.2,OpenVX1.2" +FEATURES_SOC:mx8mm-nxp-bsp = ",OpenCV4" +FEATURES_SOC:mx95-nxp-bsp = ",OpenCV4,Vulkan1.2,OpenGLES3.2,OpenCL1.2" + +EXTENSIONS = "*" +EXTENSIONS:mx6q-nxp-bsp = "OpenGLES:GL_VIV_direct_texture,OpenGLES3:GL_EXT_geometry_shader,OpenGLES3:GL_EXT_tessellation_shader" +EXTENSIONS:mx6dl-nxp-bsp = "OpenGLES:GL_VIV_direct_texture,OpenGLES3:GL_EXT_geometry_shader,OpenGLES3:GL_EXT_tessellation_shader" +EXTENSIONS:mx8m-nxp-bsp = "OpenGLES:GL_VIV_direct_texture,OpenGLES3:GL_EXT_color_buffer_float" +EXTENSIONS:mx8mm-nxp-bsp = "*" +EXTENSIONS:mx95-nxp-bsp = "OpenGLES3:GL_EXT_color_buffer_float,OpenGLES3:GL_EXT_geometry_shader,OpenGLES3:GL_EXT_tessellation_shader" + +do_compile () { + export FSL_PLATFORM_NAME=Yocto + export ROOTFS=${STAGING_DIR_HOST} + export FSL_HOST_ROOTFS=${STAGING_DIR_NATIVE} + . ./prepare.sh + FslBuild.py -vvvvv -t sdk -c install \ + --BuildThreads ${@oe.utils.parallel_make(d)} \ + --CMakeInstallPrefix ${S} \ + --UseFeatures [${FEATURES}] \ + --UseExtensions [${EXTENSIONS}] \ + --Variants [WindowSystem=${WINDOW_SYSTEM}] +} + +REMOVALS = " \ + GLES2/DeBayer \ + GLES2/DirectMultiSamplingVideoYUV \ + GLES3/DirectMultiSamplingVideoYUV \ +" +REMOVALS:append:imxdpu = " \ + G2D/EightLayers \ +" +REMOVALS:append:mx6q-nxp-bsp = " \ + GLES3/HDR02_FBBasicToneMapping \ + GLES3/HDR03_SkyboxTonemapping \ + GLES3/HDR04_HDRFramebuffer \ +" +REMOVALS:append:mx6dl-nxp-bsp = " \ + GLES3/HDR02_FBBasicToneMapping \ + GLES3/HDR03_SkyboxTonemapping \ + GLES3/HDR04_HDRFramebuffer \ +" + +do_install () { + install -d "${D}/opt/${PN}" + cp -r ${S}/bin/* ${D}/opt/${PN} + for removal in ${REMOVALS}; do + rm -rf ${D}/opt/${PN}/$removal + done +} + +FILES:${PN} += "/opt/${PN}" +FILES:${PN}-dbg += "/opt/${PN}/*/*/.debug /usr/src/debug" +INSANE_SKIP:${PN} += "already-stripped rpaths" + +RDEPENDS:${PN} += " \ + ${RDEPENDS_EMPTY_MAIN_PACKAGE} \ + ${RDEPENDS_VULKAN_LOADER}" +# Unfortunately recipes with an empty main package, like header-only libraries, +# are not included in the SDK. Use RDEPENDS as a workaround. +RDEPENDS_EMPTY_MAIN_PACKAGE = " \ + fmt \ + gli \ + glm \ + googletest \ + half \ + nlohmann-json \ + pugixml \ + rapidjson \ + stb \ + ${RDEPENDS_EMPTY_MAIN_PACKAGE_SOC}" +RDEPENDS_EMPTY_MAIN_PACKAGE_SOC = "" +RDEPENDS_EMPTY_MAIN_PACKAGE_SOC:mx8-nxp-bsp = " \ + rapidopencl \ + rapidopenvx \ + rapidvulkan" +RDEPENDS_EMPTY_MAIN_PACKAGE_SOC:mx8mm-nxp-bsp = "" +RDEPENDS_EMPTY_MAIN_PACKAGE_SOC:mx95-nxp-bsp = " \ + rapidopencl \ + rapidvulkan" +# vulkan-loader is dynamically loaded, so need to add an explicit +# dependency +RDEPENDS_VULKAN_LOADER = "" +RDEPENDS_VULKAN_LOADER:mx8-nxp-bsp = " \ + vulkan-loader" +RDEPENDS_VULKAN_LOADER:mx8mm-nxp-bsp = "" +RDEPENDS_VULKAN_LOADER:mx95-nxp-bsp = " \ + vulkan-loader" + +# For backwards compatibility +RPROVIDES:${PN} = "fsl-gpu-sdk" +RREPLACES:${PN} = "fsl-gpu-sdk" +RCONFLICTS:${PN} = "fsl-gpu-sdk" + +COMPATIBLE_MACHINE = "(imxgpu)" From 63e703161414ec1d7dee3e0a71b5d017e705e9eb Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Fri, 9 May 2025 11:08:20 +0200 Subject: [PATCH 088/138] add missing `pn-` prefix to INSANE_SKIP appends Commit 9c3916da9491262577a4c9315bf481b0b9ec5291 added INSANE_SKIP "32bit-time" to certain recipes that use 32bit APIs on the ccmp1 SOMs, but forgot to include the `pn-` prefix to really apply to those recipes. While on it, add two additional ones on recipes used by NXP 32-bit platforms. Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/include/ccmp1.inc | 8 ++++---- .../gstreamer/imx-gst1.0-plugin_%.bbappend | 2 ++ .../recipes-multimedia/imx-codec/imx-codec_%.bbappend | 4 +++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/meta-digi-arm/conf/machine/include/ccmp1.inc b/meta-digi-arm/conf/machine/include/ccmp1.inc index 2ea163719..0a58be9f2 100644 --- a/meta-digi-arm/conf/machine/include/ccmp1.inc +++ b/meta-digi-arm/conf/machine/include/ccmp1.inc @@ -106,7 +106,7 @@ SWUPDATE_MTD_BLACKLIST = "0 1 2 3 4 5" ST_TOOLS_FOR_TARGET_SDK:remove = "kernel-headers" # Skip warnings for packages using 32-bit APIs -INSANE_SKIP:append:alsa-lib = " 32bit-time" -INSANE_SKIP:append:ir-keytable = " 32bit-time" -INSANE_SKIP:append:kbd = " 32bit-time" -INSANE_SKIP:append:v4l-utils = " 32bit-time" +INSANE_SKIP:append:pn-alsa-lib = " 32bit-time" +INSANE_SKIP:append:pn-ir-keytable = " 32bit-time" +INSANE_SKIP:append:pn-kbd = " 32bit-time" +INSANE_SKIP:append:pn-v4l-utils = " 32bit-time" diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/imx-gst1.0-plugin_%.bbappend b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/imx-gst1.0-plugin_%.bbappend index 7d047b0a5..f972f8e44 100644 --- a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/imx-gst1.0-plugin_%.bbappend +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/gstreamer/imx-gst1.0-plugin_%.bbappend @@ -10,3 +10,5 @@ SRC_URI:append:ccimx6ul = " \ SRCBRANCH = "MM_04.09.02_2410_L6.6.y" SRCREV = "ef9c1a080e739e6f0be878148d9f4a050dc83bec" + +INSANE_SKIP:append:mx6-nxp-bsp = " 32bit-time" diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/imx-codec/imx-codec_%.bbappend b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/imx-codec/imx-codec_%.bbappend index 4256bc956..7007f8688 100644 --- a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/imx-codec/imx-codec_%.bbappend +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/imx-codec/imx-codec_%.bbappend @@ -1,4 +1,4 @@ -# Copyright (C) 2017,2018, Digi International Inc. +# Copyright (C) 2017-2025, Digi International Inc. # Empirically detected binaries that are not needed for a given platform REDUNDANT_BINS ?= "" @@ -29,3 +29,5 @@ do_install:append() { rm -f ${D}/${i} done } + +INSANE_SKIP:append:mx6-nxp-bsp = " 32bit-time" From ec52e7a19fa3dce9f41b1ee937584dfec3daa8b3 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Wed, 7 May 2025 17:17:21 +0200 Subject: [PATCH 089/138] mtd-utils: add a UBI health monitor service The ubihealthd daemon, present in mtd-utils does random scans of the UBI devices on a given interval. This helps to deal with 'read disturb' problem on NAND flashes. The service runs a ubihealthd thread for every UBI device with a read interval of 1 hour by default. Signed-off-by: Hector Palacios https://onedigi.atlassian.net/browse/DEL-9571 --- .../mtd/mtd-utils/ubihealthd-init | 38 +++++++++++++++++++ .../mtd/mtd-utils/ubihealthd.service | 13 +++++++ .../mtd/mtd-utils_git.bbappend | 32 +++++++++++++++- 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 meta-digi-dey/recipes-devtools/mtd/mtd-utils/ubihealthd-init create mode 100644 meta-digi-dey/recipes-devtools/mtd/mtd-utils/ubihealthd.service diff --git a/meta-digi-dey/recipes-devtools/mtd/mtd-utils/ubihealthd-init b/meta-digi-dey/recipes-devtools/mtd/mtd-utils/ubihealthd-init new file mode 100644 index 000000000..37c1cdb5c --- /dev/null +++ b/meta-digi-dey/recipes-devtools/mtd/mtd-utils/ubihealthd-init @@ -0,0 +1,38 @@ +#!/bin/sh + +DAEMON="/usr/sbin/ubihealthd" +CMD="$1" +INTERVAL="${2:-3600}" # Optional (default to 1 hour if not specified) + +[ -z "$CMD" ] && usage + +usage() { + echo "Usage: $0 {start|stop|restart} [interval_sec]" + exit 1 +} + +case "$CMD" in + start) + for dev in $(ls /sys/class/ubi/ | grep -E '^ubi[0-9]+$'); do + PIDFILE="/run/ubihealthd-${dev}.pid" + echo -n "Starting UBI health monitor on ${dev} (interval ${INTERVAL} s): " + start-stop-daemon -S -b -x "${DAEMON}" -m -p "${PIDFILE}" -- -d /dev/"${dev}" -i "${INTERVAL}" -f + echo "done." + done + ;; + stop) + for dev in $(ls /sys/class/ubi/ | grep -E '^ubi[0-9]+$'); do + PIDFILE="/run/ubihealthd-${dev}.pid" + start-stop-daemon -K -x "${DAEMON}" -p "${PIDFILE}" + rm -f "$PIDFILE" + done + ;; + restart) + $0 stop + sleep 1 + $0 start + ;; + *) + usage + ;; +esac diff --git a/meta-digi-dey/recipes-devtools/mtd/mtd-utils/ubihealthd.service b/meta-digi-dey/recipes-devtools/mtd/mtd-utils/ubihealthd.service new file mode 100644 index 000000000..0bd6a67fa --- /dev/null +++ b/meta-digi-dey/recipes-devtools/mtd/mtd-utils/ubihealthd.service @@ -0,0 +1,13 @@ +[Unit] +Description=UBI health monitor +After=ubi.mount +ConditionDirectoryNotEmpty=/sys/class/ubi + +[Service] +Type=oneshot +ExecStart=/etc/ubihealthd-init start +ExecStop=/etc/ubihealthd-init stop +RemainAfterExit=true + +[Install] +WantedBy=multi-user.target diff --git a/meta-digi-dey/recipes-devtools/mtd/mtd-utils_git.bbappend b/meta-digi-dey/recipes-devtools/mtd/mtd-utils_git.bbappend index 199efec5f..9bfa70e07 100644 --- a/meta-digi-dey/recipes-devtools/mtd/mtd-utils_git.bbappend +++ b/meta-digi-dey/recipes-devtools/mtd/mtd-utils_git.bbappend @@ -1,3 +1,33 @@ -# Copyright (C) 2023, Digi International Inc. +# Copyright (C) 2023-2025, Digi International Inc. PACKAGECONFIG:append = " crypto" +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +SRC_URI += " \ + file://ubihealthd-init \ + file://ubihealthd.service \ +" + +inherit systemd update-rc.d + +do_install:append:class-target() { + install -d ${D}${sysconfdir}/init.d/ + install -m 0755 ${WORKDIR}/ubihealthd-init ${D}${sysconfdir}/ + ln -sf ${sysconfdir}/ubihealthd-init ${D}${sysconfdir}/init.d/ubihealthd-init + + install -d ${D}${systemd_unitdir}/system/ + install -m 0644 ${WORKDIR}/ubihealthd.service ${D}${systemd_unitdir}/system/ +} + +FILES:mtd-utils-ubifs += " \ + ${sysconfdir}/ubihealthd-init \ + ${sysconfdir}/init.d/ubihealthd-init \ + ${systemd_unitdir}/system/ubihealthd.service \ +" + +INITSCRIPT_PACKAGES += "mtd-utils-ubifs" +INITSCRIPT_NAME:mtd-utils-ubifs = "ubihealthd-init" +INITSCRIPT_PARAMS:mtd-utils-ubifs = "start 19 2 3 4 5 . stop 21 0 1 6 ." + +SYSTEMD_PACKAGES = "mtd-utils-ubifs" +SYSTEMD_SERVICE:mtd-utils-ubifs = "ubihealthd.service" From 7d076d76a57454a7bc38d28d282fde65cc9a573c Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Mon, 12 May 2025 13:00:37 +0200 Subject: [PATCH 090/138] wpewebkit: fix using VPU also for ccimx8mm The patch we applied for the ccimx8x to enable VPU usage when playing videos under WebKit is also necessary for the ccimx8mm. Signed-off-by: Javier Viguera --- .../recipes-browser/wpewebkit/wpewebkit_2.44.4.bbappend | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/meta-digi-dey/dynamic-layers/webkit/recipes-browser/wpewebkit/wpewebkit_2.44.4.bbappend b/meta-digi-dey/dynamic-layers/webkit/recipes-browser/wpewebkit/wpewebkit_2.44.4.bbappend index 333c248d5..0df674395 100644 --- a/meta-digi-dey/dynamic-layers/webkit/recipes-browser/wpewebkit/wpewebkit_2.44.4.bbappend +++ b/meta-digi-dey/dynamic-layers/webkit/recipes-browser/wpewebkit/wpewebkit_2.44.4.bbappend @@ -10,3 +10,7 @@ SRC_URI:append = " \ SRC_URI:append:ccimx8x = " \ file://0001-DMABufVideoSinkGStreamer-disable-sink-unconditionall.patch \ " + +SRC_URI:append:ccimx8m = " \ + file://0001-DMABufVideoSinkGStreamer-disable-sink-unconditionall.patch \ +" From fdec14adc9af0cb28c00ac547378d07c397072ee Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Mon, 12 May 2025 13:19:30 +0200 Subject: [PATCH 091/138] tf-a-stm32mp: add USB boot support to 'opteemin' flavor of nand OP-TEE comes in two flavors: optee and opteemin For NAND-boot images, add support for USB boot as well, so that the default tf-a image is valid for booting from either NAND or USB. We had this for 'optee' flavor but not for 'opteemin'. Signed-off-by: Hector Palacios --- .../trusted-firmware-a/tf-a-stm32mp_2.10.bbappend | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend index 7f3e42581..5e1423811 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend @@ -30,6 +30,12 @@ TF_A_CONFIG[optee-nand] = "\ ${TF_A_CONFIG_BASENAME_BIN},\ ${TF_A_CONFIG_MAKE_TARGET},\ ${TF_A_CONFIG_DEPLOY_FTYPE} ${TF_A_CONFIG_DEPLOY_EXTRA}" +TF_A_CONFIG[opteemin-nand] ?= "\ + ${STM32MP_DT_FILES_NAND},\ + ${TF_A_CONFIG_OPTS_optee} ${TF_A_CONFIG_OPTS_features} ${TF_A_CONFIG_OPTS_fwupdate} STM32MP_RAW_NAND=1 STM32MP_USB_PROGRAMMER=1 ${@'STM32MP_FORCE_MTD_START_OFFSET=${TF_A_MTD_START_OFFSET_NAND}' if ${TF_A_MTD_START_OFFSET_NAND} else ''},\ + ${TF_A_CONFIG_BASENAME_BIN},\ + ${TF_A_CONFIG_MAKE_TARGET},\ + ${TF_A_CONFIG_DEPLOY_FTYPE} ${TF_A_CONFIG_DEPLOY_EXTRA}" # TF_A_CONFIG[uart] (same as 'optee-programmer-uart') TF_A_CONFIG[uart] ?= "\ ${STM32MP_DEVICETREE_PROGRAMMER},\ From 4f09a9f54670c7c6bf27cd7371e771a73c97f3a8 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Tue, 13 May 2025 09:42:21 +0200 Subject: [PATCH 092/138] wpewebkit: restrict VPU patch to 8M Mini The 8M Nano does not have a VPU. Signed-off-by: Javier Viguera --- .../webkit/recipes-browser/wpewebkit/wpewebkit_2.44.4.bbappend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meta-digi-dey/dynamic-layers/webkit/recipes-browser/wpewebkit/wpewebkit_2.44.4.bbappend b/meta-digi-dey/dynamic-layers/webkit/recipes-browser/wpewebkit/wpewebkit_2.44.4.bbappend index 0df674395..02094e80c 100644 --- a/meta-digi-dey/dynamic-layers/webkit/recipes-browser/wpewebkit/wpewebkit_2.44.4.bbappend +++ b/meta-digi-dey/dynamic-layers/webkit/recipes-browser/wpewebkit/wpewebkit_2.44.4.bbappend @@ -11,6 +11,6 @@ SRC_URI:append:ccimx8x = " \ file://0001-DMABufVideoSinkGStreamer-disable-sink-unconditionall.patch \ " -SRC_URI:append:ccimx8m = " \ +SRC_URI:append:ccimx8mm = " \ file://0001-DMABufVideoSinkGStreamer-disable-sink-unconditionall.patch \ " From 30990b4c2e60f95f6290c707b49b41dea75d98d2 Mon Sep 17 00:00:00 2001 From: Gabriel Valcazar Date: Mon, 12 May 2025 17:18:38 +0200 Subject: [PATCH 093/138] ccmp15: manually add "optee" to MACHINE_FEATURES STM's st-machine-common-stm32mp.inc file automatically incorporates "optee" to MACHINE_FEATURES as long as BOOTSCHEME_LABELS contains "optee". Since we recently modified the ccmp15's labels to only contain "opteemin", this is no longer the case, which leads to: * optee packages (optee-client, optee-os) not getting installed in images and SDKs * optee patches for environment encryption not being applied to libubootenv Add the feature manually to fix these two issues Signed-off-by: Gabriel Valcazar --- meta-digi-arm/conf/machine/ccmp15-dvk.conf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/meta-digi-arm/conf/machine/ccmp15-dvk.conf b/meta-digi-arm/conf/machine/ccmp15-dvk.conf index 6e20355d8..072beb818 100644 --- a/meta-digi-arm/conf/machine/ccmp15-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp15-dvk.conf @@ -80,6 +80,9 @@ BOOTSCHEME_DEFAULT ?= "opteemin" MACHINE_FEATURES += "bluetooth" MACHINE_FEATURES += "wifi" MACHINE_FEATURES += "gpu" +# When BOOTSCHEME_LABELS is set to "opteemin", optee doesn't get automatically +# added to MACHINE_FEATURES. Add it manually +MACHINE_FEATURES += "optee" # ========================================================================= # Firmware From 165b1ac4ce5bb58abc110f7d03ef50c8ee47d494 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Mon, 12 May 2025 16:48:51 +0200 Subject: [PATCH 094/138] ccmp13: add device tree overlay for DVKv3 This overlay contains a workaround to make the USB-OTG work as USB device when connected to a host. Signed-off-by: Hector Palacios https://onedigi.atlassian.net/browse/DEL-9610 (cherry picked from commit ec92f5fdd10a61e37ac3778d0d3aa1816bc6b0aa) Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/ccmp13-dvk.conf | 1 + .../recipes-bsp/u-boot/u-boot-dey/ccmp13-dvk/boot.txt | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/meta-digi-arm/conf/machine/ccmp13-dvk.conf b/meta-digi-arm/conf/machine/ccmp13-dvk.conf index 7b3504c62..00124a7bb 100644 --- a/meta-digi-arm/conf/machine/ccmp13-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp13-dvk.conf @@ -50,6 +50,7 @@ STM32MP_KERNEL_DEVICETREE:ccmp13-dvk += " \ ccmp13-dvk_mikroe-i2c-to-spi-click.dtbo \ ccmp13-dvk_pwm8.dtbo \ ccmp13-dvk_v2.dtbo \ + ccmp13-dvk_v3.dtbo \ ccmp133-dvk.dtb \ " FIT_CONF_DEFAULT_DTB = "ccmp133-dvk.dtb" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp13-dvk/boot.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp13-dvk/boot.txt index d0e7e60df..9b8006c68 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp13-dvk/boot.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp13-dvk/boot.txt @@ -66,9 +66,11 @@ if test "${module_has_wifi}" = "1" && test -z "${disable_wifi}"; then setenv overlays ccmp13_wifi.dtbo,${overlays} fi -# Apply DVKv2 overlay if the board_version is 2 +# Apply DVK version overlay if test "${board_version}" -eq "2"; then setenv overlays ccmp13-dvk_v2.dtbo,${overlays} +elif test "${board_version}" -eq "3"; then + setenv overlays ccmp13-dvk_v3.dtbo,${overlays} fi if test "${boot_device}" = "mmc"; then From 94c6e338f5c50b17931d6ca5ace63dd4044fc02b Mon Sep 17 00:00:00 2001 From: Gonzalo Ruiz Date: Mon, 12 May 2025 17:55:23 +0200 Subject: [PATCH 095/138] firmware-murata-infineon: ccmp1: update World CLM blob file Add the following countries to the CLM Blob file: - Brazil - Mexico - Saudi Arabia - Hong Kong - Singapore - Malaysia - Taiwan - Korea This is the new World CLM Blob file: - cyfmac4373-sdio_World.clm_blob (11d5fab6659eff491aca1a219ad33b00) https://onedigi.atlassian.net/browse/DEL-9438 Signed-off-by: Gonzalo Ruiz --- .../cyfmac4373-sdio_World.clm_blob | Bin 2020 -> 2780 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/meta-digi-arm/recipes-bsp/firmware-murata-infineon/firmware-murata-infineon/cyfmac4373-sdio_World.clm_blob b/meta-digi-arm/recipes-bsp/firmware-murata-infineon/firmware-murata-infineon/cyfmac4373-sdio_World.clm_blob index 4aef50d59ca8692cbba8f332a019ddf7bb15b012..9e7e228200af39bb60a0577a9d88e884c2eaf74e 100644 GIT binary patch literal 2780 zcmbVN&2JM&6n`_bU*5Hy#7T(xh_TIL;#D!hG260@R*4-pAK1Z)1BIx!S~NtEqBg1! z2SlX^q0~x!D54Tb2&q1S#G(CLsHs_RdER&6V+U;s^I za=KPO%O`y~TbMXs*fl?tG3Vy3`CGR?`E=gy3m&J-sELa*Zd z(uk3eWJT2?x)F`V`}zkGgG0k7){$Aa3)3^eB*rq+RiT%Kz6?kaGUe&OE?*FOR_G-l zl#oni8YrWbR^|eK<(kkdfu0e0GZzE7yK9MpeV9TFD`+qDoUm*)~Sm5s>rWi32VPGc6LQNzeI1kZ+8 zw^0jlmqp#nA3!V?jnc~vh{t0wdPSVi)fMRLi^pls)vj-Ak6&q&xD1abN5GRIr|kRd zPX!MTg9v~0MD|Q`3=v58#YhP3`oOLRL{s#kW4SSl83ct9gT$yVX%WM~D`%SKxH)BR znD@<=*)}_4<71Ph$uofwl>?FomQ*eZg(C% zf7I!8isQv|r^idDixcT_+bNxQCTy@QYt6cAZCOvO7ml;GwzhS5YwHQh3x4l^z!#6} zA5l~25)*H!&8W*5K`TY6;sLb?Tac(HGfQEfO5Z%g5r%dV0J@00AC+4p7~wIx$XIAC z_}0JU9^J)#9dU2X5OH4(WC7p7HfD0~^I|!9r+>8@mf32c!faakeqy_tRB$ zSWf@iR{xV1{Wup2@3jx{Jl{wCB@S~+YPO|E9o}Pa+z_zi=0aD0#dzrl$+h?Q0V zZlbSW1DL~p_#Tcr*4?dtAoO6^T=bXW|Hi-z$Z5r4b`_G?g{;R4VJl9wv5{Wb;HOT{dDQM^kyK zx)I_fhvUjC{aLEWRSsozXU`VWIRi2oNAuk{P&>Po8 zBE0-#sj;}#LnY)F0{EnLwQkFfE!g#T}`o(|=t zUk-64+;a!_{B6$&FCQ-)H{8qlC1^phwqL9r9B3k_sL1#c0DjHU?1W4ZW4P5 zGjh3HA$KqLEcdEVDA*{jy>8#LAGp`AZ?CVf-+S<2dwcuY<7Yw6FC2j42c2snpH%?< E0Sc2V=l}o! literal 2020 zcma)6O=x3P6h8O9pS&h%nx^eEooQo|K0^sMlVmba4Z|Zp?d!BAozk?!Ojj;K?UZR9 zw200^5K1XElq@V2Dip!2Mwo?LSFT*nN)%iSxD^C9x)9=b-@8exj5vqfeD9p|o$sA@ z-o39-cFLX74f zyz?MatJ@j^V;~T~>o-j^YnIGCvt_o;&P;aZR-shb zE3`_bX0y51Y&F|kds~P5hx@Jl_I{_;YPZ{+lP^y?olZWRFU)51v-w;+d$X8(vzWIi zolbA3AE%GfXXz)!;`a9T(c`0|GuRVv@BN^7_!E#*mzmgUmV)*VzPl5i6b7Az3 zipH_M86%x*8e=+F;c%boMhCoH5FT>q2)`Gcu}iWlPK(ml#yDsijj%!Hem{Q2b-Ebd zJ_?^b2_x4t(d!||H-MjFnI*N^R!=(OoAL-Nssll4Vm;T0J_q+NTnBOe8Ze9e8z9eM zy^ZJWu6&~10}9&$J_ASChgd(0reMEG{1&+uIEJW)P8jP)=)R174_+MnXFvn^1$4G# zVHSF$Sl*jeSyvqo#KvP&rpeWcpIIZ!nqo4 zWW_jhl1yh^1@+O8$rVvODoPdJS0BzQPAh9x~aGyU)JHT*dglk@LMQB0?p1CSFwdlkjy+ zgsc&=rYLEWPc3q?46;I$940@v7$pOwV>aXrlRZY(6>_eUGf7sQoFw`5jQd^4_{k1Z zCPa1^Re45vKk1kPS);D3B4h`oCU-U6Y=o93$fs10^^qMQzfvKxbSfJESB`mJYU$bw z`Wfu?!}2QW7%)}Bq8}&Wwq&65qAG5N<}Hsr Date: Wed, 14 May 2025 15:17:22 +0200 Subject: [PATCH 096/138] global: remove recipes related to outdated features There are several recipes in meta-digi related to features that we haven't tested in a long time and don't appear in the DEY 5.0 documentation. Remove them to avoid unexpected behavior. Said features are: * Coral TPU support (only supported in DEY 3.2) * AWS support (removed from default images and docs in DEY 4.0) * dey-image-tiny (hasn't been maintained since DEY 2.0) Signed-off-by: Gabriel Valcazar --- .../recipes-bsp/libedgetpu/libedgetpu_13.0.bb | 37 ----- ...er-fix-video-sink-for-wayland-images.patch | 29 ---- .../libedgetpu/libedgetpu-bodypix_git.bb | 36 ----- .../libedgetpu/libedgetpu-camera_git.bb | 35 ----- .../libedgetpu/libedgetpu-keyword_git.bb | 33 ---- .../tensorflow-lite-coral_2.5.0.bb | 49 ------ meta-digi-dey/classes/aws-iot.bbclass | 110 -------------- .../python3-pycoral/python3-pycoral_1.0.1.bb | 68 --------- .../images/dey-image-aws.bbappend | 1 - .../awsiotsdk-c/awsiotsdk-c-2.1.1/Makefile | 31 ---- .../awsiotsdk-c-2.1.1/Makefile.lib | 79 ---------- .../aws_iot_config.h.template | 98 ------------ .../awsiotsdk-c-2.1.1/awsiotsdk.pc | 12 -- .../awsiotsdk-c/awsiotsdk-c_2.1.1.bb | 73 --------- .../recipes-aws/greengrass/greengrass.inc | 141 ------------------ .../greengrass/greengrass/greengrass-init | 31 ---- .../greengrass/greengrass/greengrass.service | 10 -- .../greengrass/greengrass_1.10.0.bb | 29 ---- .../greengrass/greengrass_1.6.0.bb | 90 ----------- .../greengrass/greengrass_1.7.0.bb | 29 ---- .../greengrass/greengrass_1.8.0.bb | 29 ---- .../greengrass/greengrass_1.9.2.bb | 29 ---- .../recipes-core/images/dey-image-aws.bb | 29 ---- .../images/dey-image-tiny-initramfs.bb | 11 -- .../recipes-core/images/dey-image-tiny.bb | 25 ---- .../python3-svgwrite_1.4.3.bb | 13 -- .../dey-examples/awsiotsdk-demo_git.bb | 43 ------ .../swu-images/dey-image-aws-swu.bb | 3 - scripts/dey-aws-tool.sh | 141 ------------------ 29 files changed, 1344 deletions(-) delete mode 100644 meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/libedgetpu/libedgetpu_13.0.bb delete mode 100644 meta-digi-arm/recipes-bsp/libedgetpu/files/0001-gstreamer-fix-video-sink-for-wayland-images.patch delete mode 100644 meta-digi-arm/recipes-bsp/libedgetpu/libedgetpu-bodypix_git.bb delete mode 100644 meta-digi-arm/recipes-bsp/libedgetpu/libedgetpu-camera_git.bb delete mode 100644 meta-digi-arm/recipes-bsp/libedgetpu/libedgetpu-keyword_git.bb delete mode 100644 meta-digi-arm/recipes-bsp/tensorflow-lite/tensorflow-lite-coral_2.5.0.bb delete mode 100644 meta-digi-dey/classes/aws-iot.bbclass delete mode 100644 meta-digi-dey/dynamic-layers/freescale-layer/recipes-devtools/python3-pycoral/python3-pycoral_1.0.1.bb delete mode 100644 meta-digi-dey/dynamic-layers/selinux/recipes-core/images/dey-image-aws.bbappend delete mode 100644 meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c-2.1.1/Makefile delete mode 100644 meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c-2.1.1/Makefile.lib delete mode 100644 meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c-2.1.1/aws_iot_config.h.template delete mode 100644 meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c-2.1.1/awsiotsdk.pc delete mode 100644 meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c_2.1.1.bb delete mode 100644 meta-digi-dey/recipes-aws/greengrass/greengrass.inc delete mode 100644 meta-digi-dey/recipes-aws/greengrass/greengrass/greengrass-init delete mode 100644 meta-digi-dey/recipes-aws/greengrass/greengrass/greengrass.service delete mode 100644 meta-digi-dey/recipes-aws/greengrass/greengrass_1.10.0.bb delete mode 100644 meta-digi-dey/recipes-aws/greengrass/greengrass_1.6.0.bb delete mode 100644 meta-digi-dey/recipes-aws/greengrass/greengrass_1.7.0.bb delete mode 100644 meta-digi-dey/recipes-aws/greengrass/greengrass_1.8.0.bb delete mode 100644 meta-digi-dey/recipes-aws/greengrass/greengrass_1.9.2.bb delete mode 100644 meta-digi-dey/recipes-core/images/dey-image-aws.bb delete mode 100644 meta-digi-dey/recipes-core/images/dey-image-tiny-initramfs.bb delete mode 100644 meta-digi-dey/recipes-core/images/dey-image-tiny.bb delete mode 100644 meta-digi-dey/recipes-devtools/python3-svgwrite/python3-svgwrite_1.4.3.bb delete mode 100644 meta-digi-dey/recipes-digi/dey-examples/awsiotsdk-demo_git.bb delete mode 100644 meta-digi-dey/recipes-digi/swu-images/dey-image-aws-swu.bb delete mode 100755 scripts/dey-aws-tool.sh diff --git a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/libedgetpu/libedgetpu_13.0.bb b/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/libedgetpu/libedgetpu_13.0.bb deleted file mode 100644 index be6e2fa84..000000000 --- a/meta-digi-arm/dynamic-layers/freescale-layer/recipes-bsp/libedgetpu/libedgetpu_13.0.bb +++ /dev/null @@ -1,37 +0,0 @@ -SUMMARY = "Edge TPU runtime library for Coral devices" -HOMEPAGE = "https://coral.googlesource.com/edgetpu" -LICENSE = "Apache-2.0" -LIC_FILES_CHKSUM = "file://libedgetpu/LICENSE.txt;md5=c0e85c67b919e863a1a7a3da109dc40d" - -SRC_URI = "https://dl.google.com/coral/edgetpu_api/edgetpu_runtime_20210119.zip" -SRC_URI[md5sum] = "5c0b992d73683e395d6993761064d2df" -SRC_URI[sha256sum] = "b23b2c5a227d7f0e65dcc91585028d27c12e764f8ce4c4db3f114be4a49af3ae" - -S = "${WORKDIR}/edgetpu_runtime" - -RDEPENDS:${PN} = "libusb1" - -# The library files in direct correspond to max frequency, those in throttled correspond to reduced frequency. -LIBEDGETPU_TYPE = "direct" -LIBEDGETPU_ARCH = "aarch64" - -do_install() { - install -d ${D}${sysconfdir}/udev/rules.d - install -m 0644 ${S}/libedgetpu/edgetpu-accelerator.rules \ - ${D}${sysconfdir}/udev/rules.d/99-edgetpu-accelerator.rules - - install -d ${D}/${libdir} - install -m 755 ${S}/libedgetpu/${LIBEDGETPU_TYPE}/${LIBEDGETPU_ARCH}/libedgetpu.so.1.0 \ - ${D}/${libdir}/libedgetpu.so.1.0 - ln -sf ${libdir}/libedgetpu.so.1.0 ${D}/${libdir}/libedgetpu.so.1 - ln -sf ${libdir}/libedgetpu.so.1.0 ${D}/${libdir}/libedgetpu.so - - install -d ${D}/${includedir} - install -m 755 ${S}/libedgetpu/edgetpu.h ${D}/${includedir}/edgetpu.h -} - -FILES:${PN} += "${libdir}/libedgetpu.so \ - ${includedir}/edgetpu.h \ -" - -INSANE_SKIP:${PN} += "already-stripped" diff --git a/meta-digi-arm/recipes-bsp/libedgetpu/files/0001-gstreamer-fix-video-sink-for-wayland-images.patch b/meta-digi-arm/recipes-bsp/libedgetpu/files/0001-gstreamer-fix-video-sink-for-wayland-images.patch deleted file mode 100644 index 4793a565b..000000000 --- a/meta-digi-arm/recipes-bsp/libedgetpu/files/0001-gstreamer-fix-video-sink-for-wayland-images.patch +++ /dev/null @@ -1,29 +0,0 @@ -From: Arturo Buzarra -Date: Mon, 28 Jun 2021 15:00:58 +0200 -Subject: [PATCH] gstreamer: fix video sink for wayland images - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Arturo Buzarra ---- - gstreamer/gstreamer.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/gstreamer/gstreamer.py b/gstreamer/gstreamer.py -index 2676439..e055d77 100644 ---- a/gstreamer/gstreamer.py -+++ b/gstreamer/gstreamer.py -@@ -249,11 +249,11 @@ def run_pipeline(user_function, - scale_caps = 'video/x-raw,width={width},height={height}'.format(width=scale[0], height=scale[1]) - PIPELINE += """ ! tee name=t - t. ! {leaky_q} ! videoconvert ! videoscale ! {scale_caps} ! videobox name=box autocrop=true - ! {sink_caps} ! {sink_element} - t. ! {leaky_q} ! videoconvert -- ! rsvgoverlay name=overlay ! videoconvert ! ximagesink sync=false -+ ! rsvgoverlay name=overlay ! videoconvert ! waylandsink sync=false - """ - - SINK_ELEMENT = 'appsink name=appsink emit-signals=true max-buffers=1 drop=true' - SINK_CAPS = 'video/x-raw,format=RGB,width={width},height={height}' - LEAKY_Q = 'queue max-size-buffers=1 leaky=downstream' --- diff --git a/meta-digi-arm/recipes-bsp/libedgetpu/libedgetpu-bodypix_git.bb b/meta-digi-arm/recipes-bsp/libedgetpu/libedgetpu-bodypix_git.bb deleted file mode 100644 index c80d16d42..000000000 --- a/meta-digi-arm/recipes-bsp/libedgetpu/libedgetpu-bodypix_git.bb +++ /dev/null @@ -1,36 +0,0 @@ -SUMMARY = "Edge TPU keyphrase detector" -HOMEPAGE = "https://coral.ai/examples" -LICENSE = "Apache-2.0" -LIC_FILES_CHKSUM = "file://LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57" - -SRCREV = "655a354df5f939602ff6b9da2cbc4d2c78898107" -SRC_URI = "git://github.com/google-coral/project-bodypix.git;protocol=https;branch=master" - -S = "${WORKDIR}/git" - -RDEPENDS:${PN} = "python3-pycoral \ - python3-svgwrite \ - python3-scipy \ - libusb1 \ -" - -do_install() { - # Install Gstreamer examples - install -d ${D}/opt/libedgetpu - install -d ${D}/opt/libedgetpu/bodypix - install -d ${D}/opt/libedgetpu/bodypix/models - install -d ${D}/opt/libedgetpu/bodypix/posenet_lib - install -d ${D}/opt/libedgetpu/bodypix/posenet_lib/aarch64 - - install -m 0555 ${S}/models/bodypix_mobilenet* ${D}/opt/libedgetpu/bodypix/models/ - install -m 0555 ${S}/bodypix.py ${D}/opt/libedgetpu/bodypix - install -m 0555 ${S}/gstreamer.py ${D}/opt/libedgetpu/bodypix - install -m 0555 ${S}/pose_engine.py ${D}/opt/libedgetpu/bodypix - - install -m 0555 ${S}/posenet_lib/aarch64/posenet_decoder.so ${D}/opt/libedgetpu/bodypix/posenet_lib/aarch64 -} - -FILES:${PN} += "/opt/libedgetpu/bodypix/* \ -" - -INSANE_SKIP:${PN} += "already-stripped" diff --git a/meta-digi-arm/recipes-bsp/libedgetpu/libedgetpu-camera_git.bb b/meta-digi-arm/recipes-bsp/libedgetpu/libedgetpu-camera_git.bb deleted file mode 100644 index 95c0e3a5d..000000000 --- a/meta-digi-arm/recipes-bsp/libedgetpu/libedgetpu-camera_git.bb +++ /dev/null @@ -1,35 +0,0 @@ -SUMMARY = "Edge TPU simple camera examples" -HOMEPAGE = "https://coral.ai/examples" -LICENSE = "Apache-2.0" -LIC_FILES_CHKSUM = "file://LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57" - -SRCREV = "19335531f599133e054ec2ddcc31733d24031ba5" -SRC_URI = "git://github.com/google-coral/examples-camera.git;protocol=https;branch=master \ - file://0001-gstreamer-fix-video-sink-for-wayland-images.patch \ - " - -S = "${WORKDIR}/git" - -inherit gobject-introspection - -RDEPENDS:${PN} = "python3-pycoral \ - gstreamer1.0-plugins-base \ -" - -do_configure() { - bash download_models.sh -} - -do_install() { - # Install Gstreamer examples - install -d ${D}/opt/libedgetpu - install -d ${D}/opt/libedgetpu/camera - install -d ${D}/opt/libedgetpu/camera/all_models - install -d ${D}/opt/libedgetpu/camera/gstreamer - rm -f ${S}/gstreamer/install_requirements.sh - install -m 0555 ${S}/all_models/* ${D}/opt/libedgetpu/camera/all_models/ - install -m 0555 ${S}/gstreamer/* ${D}/opt/libedgetpu/camera/gstreamer/ -} - -FILES:${PN} += "/opt/libedgetpu/camera/* \ -" diff --git a/meta-digi-arm/recipes-bsp/libedgetpu/libedgetpu-keyword_git.bb b/meta-digi-arm/recipes-bsp/libedgetpu/libedgetpu-keyword_git.bb deleted file mode 100644 index f26504ee9..000000000 --- a/meta-digi-arm/recipes-bsp/libedgetpu/libedgetpu-keyword_git.bb +++ /dev/null @@ -1,33 +0,0 @@ -SUMMARY = "Edge TPU keyphrase detector" -HOMEPAGE = "https://coral.ai/examples" -LICENSE = "Apache-2.0" -LIC_FILES_CHKSUM = "file://LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57" - -SRCREV = "43a5fd0578c75c9140b4f057de74f2dbac96ceff" -SRC_URI = "git://github.com/google-coral/project-keyword-spotter.git;protocol=https;branch=master" - -S = "${WORKDIR}/git" - -RDEPENDS:${PN} = "python3-pycoral \ - python3-pyaudio \ -" - -do_install() { - # Install Gstreamer examples - install -d ${D}/opt/libedgetpu - install -d ${D}/opt/libedgetpu/keyword - install -d ${D}/opt/libedgetpu/keyword/models - install -d ${D}/opt/libedgetpu/keyword/config - - install -m 0555 ${S}/models/* ${D}/opt/libedgetpu/keyword/models/ - install -m 0555 ${S}/run_model.py ${D}/opt/libedgetpu/keyword - install -m 0555 ${S}/mel_features.py ${D}/opt/libedgetpu/keyword - install -m 0555 ${S}/model.py ${D}/opt/libedgetpu/keyword - install -m 0555 ${S}/audio_recorder.py ${D}/opt/libedgetpu/keyword - - install -m 0555 ${S}/config/labels_gc2.raw.txt ${D}/opt/libedgetpu/keyword/config - install -m 0555 ${S}/config/commands_v2.txt ${D}/opt/libedgetpu/keyword/config -} - -FILES:${PN} += "/opt/libedgetpu/keyword/* \ -" diff --git a/meta-digi-arm/recipes-bsp/tensorflow-lite/tensorflow-lite-coral_2.5.0.bb b/meta-digi-arm/recipes-bsp/tensorflow-lite/tensorflow-lite-coral_2.5.0.bb deleted file mode 100644 index 0b3cfcc80..000000000 --- a/meta-digi-arm/recipes-bsp/tensorflow-lite/tensorflow-lite-coral_2.5.0.bb +++ /dev/null @@ -1,49 +0,0 @@ -SUMMARY = "TensorFlow Lite Python Library" -DESCRIPTION = "TensorFlow Lite is the official solution for running machine learning models on mobile and embedded devices." -SECTION = "devel/python" -HOMEPAGE = "https://www.tensorflow.org/lite/" -LICENSE = "Apache-2.0" -LIC_FILES_CHKSUM = "file://${S}/tflite_runtime-2.5.0.dist-info/METADATA;md5=8c4b9e107a64b0121980a8705094014b" - -PYTHON_PACKAGE = "tflite_runtime-2.5.0-cp38-cp38-linux_aarch64.whl" - -SRC_URI = "https://github.com/google-coral/pycoral/releases/download/v1.0.1/${PYTHON_PACKAGE};downloadfilename=${PYTHON_PACKAGE};subdir=${BP}" -SRC_URI[md5sum] = "9c47617e1fa0bdca673a78b8b6688d64" -SRC_URI[sha256sum] = "b87a4c152be05d3585521a1d5418f7645a4fb82965772489b983e93aae6bd9ac" - -DEPENDS = "python3 python3-pip-native python3-wheel-native" - -RDEPENDS:${PN} = "${PYTHON_PN} \ - ${PYTHON_PN}-numpy \ -" - -inherit python3native - -do_unpack[depends] += "unzip-native:do_populate_sysroot" - -do_unpack_extra(){ - [ -d ${S} ] || mkdir -p ${S} - cd ${S} - unzip -q -o ${S}/${PYTHON_PACKAGE} -d ${S} -} -addtask unpack_extra after do_unpack before do_patch - -do_install() { - # Install pip package - install -d ${D}/${PYTHON_SITEPACKAGES_DIR} - ${STAGING_BINDIR_NATIVE}/pip3 install --disable-pip-version-check -v \ - -t ${D}/${PYTHON_SITEPACKAGES_DIR} --no-cache-dir --no-deps \ - ${WORKDIR}/${BP}/tflite_runtime-*.whl -} - -FILES:${PN} += "\ - ${libdir}/${PYTHON_DIR}/site-packages/* \ -" - -INSANE_SKIP:${PN} += "already-stripped" - -# This package provides the same python files as NXP's tensorflow-lite -RCONFLICTS:${PN} = "tensorflow-lite" - -COMPATIBLE_MACHINE = "(-)" -COMPATIBLE_MACHINE:aarch64 = "(.*)" diff --git a/meta-digi-dey/classes/aws-iot.bbclass b/meta-digi-dey/classes/aws-iot.bbclass deleted file mode 100644 index 12bdc12a5..000000000 --- a/meta-digi-dey/classes/aws-iot.bbclass +++ /dev/null @@ -1,110 +0,0 @@ -# Adds AWS IoT device SDK for embedded C configuration -# - -# Customer specific MQTT HOST -AWS_IOT_MQTT_HOST ?= "" - -# Default port for MQTT/S -AWS_IOT_MQTT_PORT ?= "8883" - -# Thing Name of the Shadow the device is associated with -AWS_IOT_MY_THING_NAME ?= "AWS-IoT-C-SDK" - -# Root CA file name -AWS_IOT_ROOT_CA_FILENAME ?= "rootCA.crt" - -# Device signed certificate file name -AWS_IOT_CERTIFICATE_FILENAME ?= "cert.pem" - -# Device private key filename -AWS_IOT_PRIVATE_KEY_FILENAME ?= "privkey.pem" - -# MQTT PubSub - -# Any time a message is sent out through the MQTT layer. The message is copied -# into this buffer anytime a publish is done. -# This will also be used in the case of Thing Shadow -AWS_IOT_MQTT_TX_BUF_LEN ?= "512" - -# Any message that comes into the device should be less than this buffer size. -# If a received message is bigger than this buffer size the message will be -# dropped -AWS_IOT_MQTT_RX_BUF_LEN ?= "512" - -# Maximum number of topic filters the MQTT client can handle at any given time. -# This should be increased appropriately when using Thing Shadow -AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS ?= "5" - -# Thing Shadow specific configs - -# At any given time we will wait for this many responses. -# This will correlate to the rate at which the shadow actions are requested -MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME ?= "10" - -# We could perform shadow action on any thing Name and this is maximum Thing -# Names we can act on at any given time -MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME ?= "10" - -# These are the max tokens that is expected to be in the Shadow JSON document. -# It includes the metadata that gets published -MAX_JSON_TOKEN_EXPECTED ?= "120" - -# The Thing Name should not be bigger than this value. Modify this if the Thing -# Name needs to be bigger -MAX_SIZE_OF_THING_NAME ?= "20" - -# Auto Reconnect specific config - -# Minimum time before the First reconnect attempt is made as part of the -# exponential back-off algorithm (milliseconds) -AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL ?= "1000" - -# Maximum time interval after which exponential back-off will stop attempting -# to reconnect (milliseconds) -AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL ?= "128000" - -# Logging level control: error, warn, info, debug, trace. -AWS_IOT_LOGGING_LEVEL ?= "debug" - -def get_log_level(d): - levels = ['error', 'warn', 'info', 'debug', 'trace'] - log_flags = "" - - log_level = d.getVar('AWS_IOT_LOGGING_LEVEL') - if log_level == 'none': - return "" - if log_level not in levels: - log_level = "debug" - d.setVar('AWS_IOT_LOGGING_LEVEL', log_level) - - log_index = levels.index(log_level) - for i, val in enumerate(levels): - log_flags = log_flags + "-DENABLE_IOT_" + val.upper() + " " - if i == log_index: - break; - - return log_flags - -####################### -# AWS Greengrass Core # -####################### - -# -# Verisign root CA server certificate used to authenticate the AWS IoT server. -# -# https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem -# -AWS_GGCORE_ROOT_CA ?= "aws-root-ca.pem" - -# Greengrass core device certificate -AWS_GGCORE_CERTIFICATE ?= "gg-core.pem" - -# Greengrass core device private key -AWS_GGCORE_PRIVATE_KEY ?= "gg-core.key" - -# Greengrass core Thing ARN -AWS_GGCORE_THING_ARN ?= "" - -# AWS IoT endpoint (check your account) -# CLI: aws iot describe-endpoint -AWS_GGCORE_IOT_HOST ?= "" diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-devtools/python3-pycoral/python3-pycoral_1.0.1.bb b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-devtools/python3-pycoral/python3-pycoral_1.0.1.bb deleted file mode 100644 index 8f540fad3..000000000 --- a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-devtools/python3-pycoral/python3-pycoral_1.0.1.bb +++ /dev/null @@ -1,68 +0,0 @@ -SUMMARY = "Python Library for Coral devices" -DESCRIPTION = "Python Library to run inferences and perform on-device transfer learning with TensorFlow Lite models on Coral devices" -SECTION = "devel/python" -HOMEPAGE = "https://coral.ai/software/#pycoral-api" -LICENSE = "Apache-2.0" -LIC_FILES_CHKSUM = "file://${S}/pycoral-1.0.1.dist-info/LICENSE;md5=d8927f3331d2b3e321b7dd1925166d25" - -PYTHON_PACKAGE = "pycoral-1.0.1-cp38-cp38-linux_aarch64.whl" - -SRC_URI = "git://github.com/google-coral/pycoral.git;protocol=https;branch=master" -SRCREV = "d4b9f572fa3baef9d854483a171e02a6b3f9dbd0" - -SRC_URI += "https://github.com/google-coral/pycoral/releases/download/v1.0.1/${PYTHON_PACKAGE};downloadfilename=${PYTHON_PACKAGE};subdir=${BP};name=python-wheel" -SRC_URI[python-wheel.md5sum] = "ea89677a47d7d81d2558b8dbbae44d95" -SRC_URI[python-wheel.sha256sum] = "894468447192fbcf946157db0f3b6424ece6d70bcec1243892d27cd7b521f176" - -DEPENDS = "python3 python3-pip-native python3-wheel-native curl-native ca-certificates-native" - -RDEPENDS:${PN} = "${PYTHON_PN} \ - ${PYTHON_PN}-numpy \ - ${PYTHON_PN}-pycairo \ - ${PYTHON_PN}-pygobject \ - ${PYTHON_PN}-pillow \ - libedgetpu \ - tensorflow-lite-coral \ -" - -inherit python3native - -do_unpack[depends] += "unzip-native:do_populate_sysroot" - -do_unpack_extra(){ - [ -d ${S} ] || mkdir -p ${S} - cd ${S} - unzip -q -o ${S}/${PYTHON_PACKAGE} -d ${S} -} -addtask unpack_extra after do_unpack before do_patch - - -do_configure() { - export CURL_CA_BUNDLE=${STAGING_DIR_NATIVE}/etc/ssl/certs/ca-certificates.crt - - cd ${WORKDIR}/git - bash examples/install_requirements.sh classify_image.py -} - -do_install() { - # Install examples - install -d ${D}/opt/pycoral - install -m 0555 ${WORKDIR}/git/test_data/* ${D}/opt/pycoral - install -m 0555 ${WORKDIR}/git/examples/classify_image.py ${D}/opt/pycoral - - # Install pip package - install -d ${D}/${PYTHON_SITEPACKAGES_DIR} - ${STAGING_BINDIR_NATIVE}/pip3 install --disable-pip-version-check -v \ - -t ${D}/${PYTHON_SITEPACKAGES_DIR} --no-cache-dir --no-deps \ - ${WORKDIR}/${BP}/pycoral-*.whl -} - -FILES:${PN} += "\ - ${libdir}/${PYTHON_DIR}/site-packages/* \ - /opt/pycoral/* \ -" - -INSANE_SKIP:${PN} += "already-stripped" - -COMPATIBLE_MACHINE = "(-)" -COMPATIBLE_MACHINE:aarch64 = "(.*)" diff --git a/meta-digi-dey/dynamic-layers/selinux/recipes-core/images/dey-image-aws.bbappend b/meta-digi-dey/dynamic-layers/selinux/recipes-core/images/dey-image-aws.bbappend deleted file mode 100644 index 072993cd7..000000000 --- a/meta-digi-dey/dynamic-layers/selinux/recipes-core/images/dey-image-aws.bbappend +++ /dev/null @@ -1 +0,0 @@ -include selinux_dey.inc diff --git a/meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c-2.1.1/Makefile b/meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c-2.1.1/Makefile deleted file mode 100644 index 6373338b7..000000000 --- a/meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c-2.1.1/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -# *************************************************************************** -# Copyright (c) 2017 Digi International Inc. -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -# PERFORMANCE OF THIS SOFTWARE. -# -# Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343 -# -# *************************************************************************** - -SUBDIRS := src - -all: $(SUBDIRS) - -.PHONY: $(SUBDIRS) -$(SUBDIRS): - $(MAKE) -C $@ - -.PHONY: clean install -clean install: - for a in $(SUBDIRS); do $(MAKE) -C $$a $@; done - diff --git a/meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c-2.1.1/Makefile.lib b/meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c-2.1.1/Makefile.lib deleted file mode 100644 index ff4ad1835..000000000 --- a/meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c-2.1.1/Makefile.lib +++ /dev/null @@ -1,79 +0,0 @@ -# *************************************************************************** -# Copyright (c) 2017 Digi International Inc. -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -# PERFORMANCE OF THIS SOFTWARE. -# -# Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343 -# -# *************************************************************************** -# Use GNU C Compiler. -CC ?= gcc - -# Generated Library name. -NAME := awsiotsdk - -# Location of Source Code. -SRC = . - -#IoT client directory. -IOT_CLIENT_DIR = $(SRC)/.. - -PLATFORM_DIR = $(IOT_CLIENT_DIR)/platform/linux/mbedtls -PLATFORM_COMMON_DIR = $(IOT_CLIENT_DIR)/platform/linux/common -INSTALL_HEADERS_DIR = /usr/include/awsiotsdk - -CFLAGS += -I $(IOT_CLIENT_DIR)/include -CFLAGS += -I $(IOT_CLIENT_DIR)/external_libs/jsmn -CFLAGS += -I $(PLATFORM_COMMON_DIR) -CFLAGS += -I $(PLATFORM_DIR) -CFLAGS += -Wall -g -CFLAGS += $(LOG_FLAGS) -CFLAGS += $(shell pkg-config --cflags mbedtls) - -# Libraries to Link. -LIBS += $(shell pkg-config --libs --static mbedtls) - -# Linking Flags. -LDFLAGS += $(DFLAGS) - -# Target output to generate. -SRCS += $(wildcard $(SRC)/*.c) -SRCS += $(wildcard $(IOT_CLIENT_DIR)/external_libs/jsmn/*.c) -SRCS += $(wildcard $(PLATFORM_DIR)/*.c) -SRCS += $(wildcard $(PLATFORM_COMMON_DIR)/*.c) - -OBJS = $(SRCS:.c=.o) - -.PHONY: all -all: lib$(NAME).a - -lib$(NAME).a: $(OBJS) - $(AR) -rcs $@ $^ - -.PHONY: install -install: lib$(NAME).a - # Install library and pkg-config file. - install -d $(DESTDIR)/usr/lib/pkgconfig - install -m 0644 lib$(NAME).a $(DESTDIR)/usr/lib/ - install -m 0644 ../awsiotsdk.pc $(DESTDIR)/usr/lib/pkgconfig/ - # Install header files. - install -d $(DESTDIR)$(INSTALL_HEADERS_DIR) - install -m 0644 $(IOT_CLIENT_DIR)/include/*.h $(DESTDIR)$(INSTALL_HEADERS_DIR)/ - install -m 0644 $(IOT_CLIENT_DIR)/external_libs/jsmn/*.h $(DESTDIR)$(INSTALL_HEADERS_DIR)/ - install -m 0644 $(PLATFORM_COMMON_DIR)/*.h $(DESTDIR)$(INSTALL_HEADERS_DIR)/ - install -m 0644 $(PLATFORM_DIR)/*.h $(DESTDIR)$(INSTALL_HEADERS_DIR)/ - -.PHONY: clean -clean: - -rm -f lib$(NAME).a $(OBJS) - diff --git a/meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c-2.1.1/aws_iot_config.h.template b/meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c-2.1.1/aws_iot_config.h.template deleted file mode 100644 index a2375a657..000000000 --- a/meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c-2.1.1/aws_iot_config.h.template +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -/** - * @file aws_iot_config.h - * @brief AWS IoT specific configuration file - */ - -#ifndef SRC_SHADOW_IOT_SHADOW_CONFIG_H_ -#define SRC_SHADOW_IOT_SHADOW_CONFIG_H_ - -/* Get from console */ -/* ================================================= */ -/* Customer specific MQTT HOST. The same will be used for Thing Shadow */ -#define AWS_IOT_MQTT_HOST "##AWS_IOT_MQTT_HOST##" -/* Default port for MQTT/S */ -#define AWS_IOT_MQTT_PORT ##AWS_IOT_MQTT_PORT## -/* Thing Name of the Shadow this device is associated with */ -#define AWS_IOT_MY_THING_NAME "##AWS_IOT_MY_THING_NAME##" -/* MQTT client ID should be unique for every device */ -#define AWS_IOT_MQTT_CLIENT_ID AWS_IOT_MY_THING_NAME -/* Root CA file name */ -#define AWS_IOT_ROOT_CA_FILENAME "##AWS_IOT_ROOT_CA_FILENAME##" -/* Device signed certificate file name */ -#define AWS_IOT_CERTIFICATE_FILENAME "##AWS_IOT_CERTIFICATE_FILENAME##" -/* Device private key filename */ -#define AWS_IOT_PRIVATE_KEY_FILENAME "##AWS_IOT_PRIVATE_KEY_FILENAME##" -/* ================================================= */ - -/* MQTT PubSub */ -/* ================================================= */ -/* Any time a message is sent out through the MQTT layer. The message is copied - * into this buffer anytime a publish is done. - * This will also be used in the case of Thing Shadow */ -#define AWS_IOT_MQTT_TX_BUF_LEN ##AWS_IOT_MQTT_TX_BUF_LEN## -/* Any message that comes into the device should be less than this buffer size. - * If a received message is bigger than this buffer size the message will be - * dropped. */ -#define AWS_IOT_MQTT_RX_BUF_LEN ##AWS_IOT_MQTT_RX_BUF_LEN## -/* Maximum number of topic filters the MQTT client can handle at any given time. - * This should be increased appropriately when using Thing Shadow */ -#define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS ##AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS## - -/* Thing Shadow specific configs */ -/* Maximum size of the Shadow buffer to store the received Shadow message */ -#define SHADOW_MAX_SIZE_OF_RX_BUFFER AWS_IOT_MQTT_RX_BUF_LEN + 1 -/* Maximum size of the Unique Client Id. - * For More info on the Client Id refer \ref response "Acknowledgments" */ -#define MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES 80 -/* This is size of the extra sequence number that will be appended to the - * Unique client Id */ -#define MAX_SIZE_CLIENT_ID_WITH_SEQUENCE MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES + 10 -/* This is size of the the total clientToken key and value pair in the JSON */ -#define MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE MAX_SIZE_CLIENT_ID_WITH_SEQUENCE + 20 -/* At any given time we will wait for this many responses. - * This will correlate to the rate at which the shadow actions are requested */ -#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME ##MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME## -/* We could perform shadow action on any thing Name and this is maximum Thing - * Names we can act on at any given time */ -#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME ##MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME## -/* These are the max tokens that is expected to be in the Shadow JSON document. - * It includes the metadata that gets published */ -#define MAX_JSON_TOKEN_EXPECTED ##MAX_JSON_TOKEN_EXPECTED## -/* All shadow actions have to be published or subscribed to a topic which is of - * the format $aws/things/{thingName}/shadow/update/accepted. - * This refers to the size of the topic without the Thing Name */ -#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME 60 -/* The Thing Name should not be bigger than this value. Modify this if the - * Thing Name needs to be bigger */ -#define MAX_SIZE_OF_THING_NAME ##MAX_SIZE_OF_THING_NAME## -/* This size includes the length of topic with Thing Name */ -#define MAX_SHADOW_TOPIC_LENGTH_BYTES MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME + MAX_SIZE_OF_THING_NAME -/* ================================================= */ - -/* Auto Reconnect specific config */ -/* ================================================= */ -/* Minimum time before the First reconnect attempt is made as part of the - * exponential back-off algorithm */ -#define AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL ##AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL## -/* Maximum time interval after which exponential back-off will stop attempting - * to reconnect */ -#define AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL ##AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL## -/* ================================================= */ - -#endif /* SRC_SHADOW_IOT_SHADOW_CONFIG_H_ */ - diff --git a/meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c-2.1.1/awsiotsdk.pc b/meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c-2.1.1/awsiotsdk.pc deleted file mode 100644 index 2c5037037..000000000 --- a/meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c-2.1.1/awsiotsdk.pc +++ /dev/null @@ -1,12 +0,0 @@ -prefix=/usr -exec_prefix=${prefix} -libdir=${exec_prefix}/lib -includedir=${prefix}/include - -Name: awsiotsdk -Description: SDK for connecting to AWS IoT from a device using embedded C -Version: 2.1.1 - -Requires.private: mbedtls -Libs: -L${libdir} -lawsiotsdk -Cflags: -I${includedir}/awsiotsdk -I${includedir} diff --git a/meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c_2.1.1.bb b/meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c_2.1.1.bb deleted file mode 100644 index 04335fe5b..000000000 --- a/meta-digi-dey/recipes-aws/awsiotsdk-c/awsiotsdk-c_2.1.1.bb +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (C) 2017, Digi International Inc. - -SUMMARY = "SDK for connecting to AWS IoT from a device using embedded C" -HOMEPAGE = "https://github.com/aws/aws-iot-device-sdk-embedded-C" -SECTION = "base" -LICENSE = "Apache-2.0" -LIC_FILES_CHKSUM = "file://LICENSE.txt;md5=acc7a1bf87c055789657b148939e4b40" - -DEPENDS = "mbedtls" - -SRC_URI = " \ - git://github.com/aws/aws-iot-device-sdk-embedded-C.git;protocol=https;branch=main \ - file://aws_iot_config.h.template \ - file://awsiotsdk.pc \ - file://Makefile \ - file://Makefile.lib \ -" - -# Tag 'v2.1.1' -SRCREV = "70071112bd5e1c5b9f150894fafe199637b4f63a" - -S = "${WORKDIR}/git" - -inherit aws-iot pkgconfig - -EXTRA_OEMAKE += "'LOG_FLAGS=${@get_log_level(d)}'" - -do_configure() { - cp -f ${WORKDIR}/awsiotsdk.pc ${S} - - # Copy and update the configuration header file. - cp -f ${WORKDIR}/aws_iot_config.h.template ${S}/include/aws_iot_config.h - sed -i -e "s,##AWS_IOT_MQTT_HOST##,${AWS_IOT_MQTT_HOST},g" "${S}/include/aws_iot_config.h" - sed -i -e "s,##AWS_IOT_MQTT_PORT##,${AWS_IOT_MQTT_PORT},g" "${S}/include/aws_iot_config.h" - sed -i -e "s,##AWS_IOT_MY_THING_NAME##,${AWS_IOT_MY_THING_NAME},g" "${S}/include/aws_iot_config.h" - sed -i -e "s,##AWS_IOT_ROOT_CA_FILENAME##,${AWS_IOT_ROOT_CA_FILENAME},g" "${S}/include/aws_iot_config.h" - sed -i -e "s,##AWS_IOT_CERTIFICATE_FILENAME##,${AWS_IOT_CERTIFICATE_FILENAME},g" "${S}/include/aws_iot_config.h" - sed -i -e "s,##AWS_IOT_PRIVATE_KEY_FILENAME##,${AWS_IOT_PRIVATE_KEY_FILENAME},g" "${S}/include/aws_iot_config.h" - sed -i -e "s,##AWS_IOT_MQTT_TX_BUF_LEN##,${AWS_IOT_MQTT_TX_BUF_LEN},g" "${S}/include/aws_iot_config.h" - sed -i -e "s,##AWS_IOT_MQTT_RX_BUF_LEN##,${AWS_IOT_MQTT_RX_BUF_LEN},g" "${S}/include/aws_iot_config.h" - sed -i -e "s,##AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS##,${AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS},g" "${S}/include/aws_iot_config.h" - sed -i -e "s,##MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME##,${MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME},g" "${S}/include/aws_iot_config.h" - sed -i -e "s,##MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME##,${MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME},g" "${S}/include/aws_iot_config.h" - sed -i -e "s,##MAX_JSON_TOKEN_EXPECTED##,${MAX_JSON_TOKEN_EXPECTED},g" "${S}/include/aws_iot_config.h" - sed -i -e "s,##MAX_SIZE_OF_THING_NAME##,${MAX_SIZE_OF_THING_NAME},g" "${S}/include/aws_iot_config.h" - sed -i -e "s,##AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL##,${AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL},g" "${S}/include/aws_iot_config.h" - sed -i -e "s,##AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL##,${AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL},g" "${S}/include/aws_iot_config.h" - - # Copy the Makefiles. - cp -f ${WORKDIR}/Makefile ${S} - cp -f ${WORKDIR}/Makefile.lib ${S}/src/Makefile -} - -do_install() { - oe_runmake DESTDIR=${D} install - - # Install certificates only if they exist. - if [ -f "${AWS_IOT_CERTS_DIR}/${AWS_IOT_ROOT_CA_FILENAME}" ] && \ - [ -f "${AWS_IOT_CERTS_DIR}/${AWS_IOT_CERTIFICATE_FILENAME}" ] && \ - [ -f "${AWS_IOT_CERTS_DIR}/${AWS_IOT_PRIVATE_KEY_FILENAME}" ]; then - install -d ${D}${sysconfdir}/ssl/certs - install -m 0644 "${AWS_IOT_CERTS_DIR}/${AWS_IOT_ROOT_CA_FILENAME}" ${D}${sysconfdir}/ssl/certs/ - install -m 0644 "${AWS_IOT_CERTS_DIR}/${AWS_IOT_CERTIFICATE_FILENAME}" ${D}${sysconfdir}/ssl/certs/ - install -m 0644 "${AWS_IOT_CERTS_DIR}/${AWS_IOT_PRIVATE_KEY_FILENAME}" ${D}${sysconfdir}/ssl/certs/ - fi -} - -PACKAGES =+ "${PN}-cert" - -FILES:${PN}-cert = "${sysconfdir}/ssl/certs/" - -ALLOW_EMPTY:${PN} = "1" - diff --git a/meta-digi-dey/recipes-aws/greengrass/greengrass.inc b/meta-digi-dey/recipes-aws/greengrass/greengrass.inc deleted file mode 100644 index efd73308d..000000000 --- a/meta-digi-dey/recipes-aws/greengrass/greengrass.inc +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright (C) 2018-2022, Digi International Inc. - -SUMMARY = "AWS IoT Greengrass core" -HOMEPAGE = "https://aws.amazon.com/greengrass/" - -CONFIGURATION_DIRECTORY = "config" - -LICENSE = "Apache-2.0 | BSD-2-Clause | BSD-3-Clause | MIT | PD | Proprietary" - -SRC_URI:arm = " \ - http:///not/exist/greengrass-linux-armv7l-${PV}.tar.gz;name=arm \ - file://greengrass.service \ - file://greengrass-init \ -" - -SRC_URI:aarch64 = " \ - http:///not/exist/greengrass-linux-aarch64-${PV}.tar.gz;name=aarch64 \ - file://greengrass.service \ - file://greengrass-init \ -" - -GG_TARBALL_LOCAL_PATH ?= "" - -# The tarball is only available for downloading after registration, so provide -# a PREMIRROR to a local directory that can be configured in the project's -# local.conf file using GG_TARBALL_LOCAL_PATH variable. -python() { - gg_tarball_local_path = d.getVar('GG_TARBALL_LOCAL_PATH') - if gg_tarball_local_path: - premirrors = d.getVar('PREMIRRORS') - d.setVar('PREMIRRORS', "http:///not/exist/greengrass.* file://%s \\n %s" % (gg_tarball_local_path, premirrors)) -} - -S = "${WORKDIR}/${BPN}" - -inherit aws-iot update-rc.d useradd systemd - -GG_USESYSTEMD = "${@bb.utils.contains('DISTRO_FEATURES', 'systemd', 'yes', 'no', d)}" - -# Disable tasks not needed for the binary package -do_configure[noexec] = "1" -do_compile[noexec] = "1" - -do_install() { - install -d ${D}/${BPN} - tar --no-same-owner --exclude='./patches' --exclude='./.pc' -cpf - -C ${S} . \ - | tar --no-same-owner -xpf - -C ${D}/${BPN} - - # Install wrapper bootscript to launch Greengrass core on boot - install -d ${D}${sysconfdir}/init.d - install -m 0755 ${WORKDIR}/greengrass-init ${D}${sysconfdir}/greengrass - sed -i -e "s,##GG_INSTALL_DIR##,/${BPN},g" ${D}${sysconfdir}/greengrass - ln -sf ${sysconfdir}/greengrass ${D}${sysconfdir}/init.d/greengrass - - # Install systemd service - install -d ${D}${systemd_unitdir}/system/ - install -m 0644 ${WORKDIR}/greengrass.service ${D}${systemd_unitdir}/system/greengrass.service - - # If certificates do exist, install them and update the config file - if [ -f "${AWS_IOT_CERTS_DIR}/${AWS_GGCORE_ROOT_CA}" ] && \ - [ -f "${AWS_IOT_CERTS_DIR}/${AWS_GGCORE_CERTIFICATE}" ] && \ - [ -f "${AWS_IOT_CERTS_DIR}/${AWS_GGCORE_PRIVATE_KEY}" ]; then - install -m 0644 "${AWS_IOT_CERTS_DIR}/${AWS_GGCORE_ROOT_CA}" \ - "${AWS_IOT_CERTS_DIR}/${AWS_GGCORE_CERTIFICATE}" \ - "${AWS_IOT_CERTS_DIR}/${AWS_GGCORE_PRIVATE_KEY}" \ - ${D}/${BPN}/certs/ - sed -i -e "s,\[ROOT_CA_PEM_HERE],${AWS_GGCORE_ROOT_CA},g" \ - -e "s,\[CLOUD_PEM_CRT_HERE],${AWS_GGCORE_CERTIFICATE},g" \ - -e "s,\[CLOUD_PEM_KEY_HERE],${AWS_GGCORE_PRIVATE_KEY},g" \ - ${D}/${BPN}/config/config.json - fi - - # Configure the rest of GG Core parameters - [ -n "${AWS_GGCORE_THING_ARN}" ] && sed -i -e "s,\[THING_ARN_HERE],${AWS_GGCORE_THING_ARN},g" ${D}/${BPN}/config/config.json - if [ -n "${AWS_GGCORE_IOT_HOST}" ]; then - AWS_GGCORE_HOST_PREFIX="$(echo ${AWS_GGCORE_IOT_HOST} | sed -e 's,\([^.]\+\)\.iot.*,\1,g')" - AWS_GGCORE_REGION="$(echo ${AWS_GGCORE_IOT_HOST} | sed -e 's,.*.iot\.\([^.]\+\)\..*,\1,g')" - [ -n "${AWS_GGCORE_HOST_PREFIX}" ] && sed -i -e "s,\[HOST_PREFIX_HERE],${AWS_GGCORE_HOST_PREFIX},g" ${D}/${BPN}/config/config.json - [ -n "${AWS_GGCORE_REGION}" ] && sed -i -e "s,\[AWS_REGION_HERE],${AWS_GGCORE_REGION},g" ${D}/${BPN}/config/config.json - fi - - # Configure whether to use systemd or not - sed -i -e "/useSystemd/{s,\[yes|no],${GG_USESYSTEMD},g}" ${D}/${BPN}/config/config.json -} - -pkg_postinst_ontarget:${PN}() { - # Enable protection for hardlinks and symlinks - if ! grep -qs 'protected_.*links' $D${sysconfdir}/sysctl.conf; then - cat >> $D${sysconfdir}/sysctl.conf <<-_EOF_ - # Greengrass: protect hardlinks/symlinks - fs.protected_hardlinks = 1 - fs.protected_symlinks = 1 - _EOF_ - fi - - # Customize '/etc/fstab' - if [ -f "$D${sysconfdir}/fstab" ]; then - # Disable TMPFS /var/volatile - sed -i -e '\#^tmpfs[[:blank:]]\+/var/volatile#s,^,#,g' $D${sysconfdir}/fstab - - # Mount a cgroup hierarchy with all available subsystems - if ! grep -qs '^cgroup' $D${sysconfdir}/fstab; then - cat >> $D${sysconfdir}/fstab <<-_EOF_ - # Greengrass: mount cgroups - cgroup /sys/fs/cgroup cgroup defaults 0 0 - _EOF_ - fi - fi - - # Disable '/etc/resolv.conf' symlink - if [ -f "$D${sysconfdir}/default/volatiles/00_core" ]; then - sed -i -e '/resolv.conf/d' $D${sysconfdir}/default/volatiles/00_core - cat >> $D${sysconfdir}/default/volatiles/00_core <<-_EOF_ - # Greengrass: create a real (no symlink) resolv.conf - f root root 0644 /etc/resolv.conf none - _EOF_ - fi -} - -FILES:${PN} = "/${BPN} ${sysconfdir} ${systemd_unitdir}" - -CONFFILES:${PN} += "/${BPN}/config/config.json" - -INITSCRIPT_NAME = "greengrass" -INITSCRIPT_PARAMS = "defaults 80 20" - -SYSTEMD_SERVICE:${PN} = "greengrass.service" - -USERADD_PACKAGES = "${PN}" -GROUPADD_PARAM:${PN} = "-r ggc_group" -USERADD_PARAM:${PN} = "-r -M -N -g ggc_group -s /bin/false ggc_user" - -# -# Disable failing QA checks: -# -# Binary was already stripped -# No GNU_HASH in the elf binary -# -INSANE_SKIP:${PN} += "already-stripped ldflags file-rdeps" - -RDEPENDS:${PN} += "ca-certificates python3-core sqlite3" diff --git a/meta-digi-dey/recipes-aws/greengrass/greengrass/greengrass-init b/meta-digi-dey/recipes-aws/greengrass/greengrass/greengrass-init deleted file mode 100644 index 2d12a1554..000000000 --- a/meta-digi-dey/recipes-aws/greengrass/greengrass/greengrass-init +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh -# -# Copyright (C) 2017, Digi International Inc. -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, you can obtain one at http://mozilla.org/MPL/2.0/. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# -# Description: Wrapper bootscript for AWS Greengrass core -# - -GG_INSTALL_DIR="##GG_INSTALL_DIR##" -GG_LAUNCHER="$(find ${GG_INSTALL_DIR} -type f -name greengrassd)" - -case "${1}" in - start | stop | restart) - ${GG_LAUNCHER} ${1} - ;; - *) - echo "Usage: $0 {start|stop|restart}" - exit 1 - ;; -esac diff --git a/meta-digi-dey/recipes-aws/greengrass/greengrass/greengrass.service b/meta-digi-dey/recipes-aws/greengrass/greengrass/greengrass.service deleted file mode 100644 index 71976e12e..000000000 --- a/meta-digi-dey/recipes-aws/greengrass/greengrass/greengrass.service +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=Greengrass core daemon - -[Service] -Type=forking -ExecStart=/etc/greengrass start -ExecStop=/etc/greengrass stop - -[Install] -WantedBy=multi-user.target diff --git a/meta-digi-dey/recipes-aws/greengrass/greengrass_1.10.0.bb b/meta-digi-dey/recipes-aws/greengrass/greengrass_1.10.0.bb deleted file mode 100644 index fe48d2914..000000000 --- a/meta-digi-dey/recipes-aws/greengrass/greengrass_1.10.0.bb +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (C) 2020, Digi International Inc. - -require greengrass.inc - -# -# The Amazon Greengrass Core Product includes the following third-party software/licensing: -# github.com/aws/aws-sdk-go/; version 1.15.65 -- https://github.com/aws/aws-sdk-go/ -# github.com/coreos/go-systemd/; version 10 -- https://github.com/coreos/go-systemd/ -# github.com/docker/docker; version 1.12.0-rc4 -- https://github.com/docker/docker -# github.com/docker/go-units; version 0.3.1 -- https://github.com/docker/go-units -# github.com/go-ini/ini; version 1.32.0 -- https://github.com/go-ini/ini -# github.com/jmespath/go-jmespath; version 0.2.2 -- https://github.com/jmespath/go-jmespath -# github.com/mwitkow/go-http-dialer; version 0.1 -- https://github.com/mwitkow/go-http-dialer -# github.com/opencontainers/runc; version 1.0.0-rc3 -- https://github.com/opencontainers/runc -# github.com/opencontainers/runtime-spec; version 1.0.0-rc5 -- https://github.com/opencontainers/runtime-spec -# github.com/pquerna/ffjson; version 1.0 -- https://github.com/pquerna/ffjson -# github.com/vishvananda/netlink; version 0.1 -- https://github.com/vishvananda/netlink -# -# And the following Licenses: -LIC_FILES_CHKSUM = " \ - file://ggc/core/THIRD-PARTY-LICENSES;md5=1f0ad815f019455e3a0efe55e888a69a \ -" - -SRC_URI[arm.md5sum] = "e54bb57929bc278ea89737c4abcd89e8" -SRC_URI[arm.sha256sum] ="91f3d92dca977ea504921c7dbae96a926adce441c8f9ec1896e4c8cf085d6d2e" - -# For ARCH64 we use another tarball. -SRC_URI[aarch64.md5sum] = "1bdde4df4c461cd5502f7adbb79b2903" -SRC_URI[aarch64.sha256sum] ="912ecbe10398382894045f9b9dafd16eac7fabce0fc04fc9ee83c8ec8f67ca5a" diff --git a/meta-digi-dey/recipes-aws/greengrass/greengrass_1.6.0.bb b/meta-digi-dey/recipes-aws/greengrass/greengrass_1.6.0.bb deleted file mode 100644 index 7a817d22c..000000000 --- a/meta-digi-dey/recipes-aws/greengrass/greengrass_1.6.0.bb +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (C) 2018,2019, Digi International Inc. - -require greengrass.inc - -# -# The package includes different licenses: -# -# [Apache-2.0] -# ggc/core/LICENSE/attributions/github_aws_aws_sdk_go_License.txt -# ggc/core/LICENSE/attributions/github_coreos_go_systemd_License.txt -# ggc/core/LICENSE/attributions/github_docker_docker_License.txt -# ggc/core/LICENSE/attributions/github_docker_go_units_License.txt -# ggc/core/LICENSE/attributions/github_go_ini_ini_License.txt -# ggc/core/LICENSE/attributions/github_jmespath_go_jmespath_License.txt -# ggc/core/LICENSE/attributions/github_opencontainers_runc_License.txt -# ggc/core/LICENSE/attributions/github_opencontainers_runtime_spec_License.txt -# ggc/core/LICENSE/attributions/github_pquerna_ffjson_License.txt -# ggc/core/LICENSE/attributions/github_vishvananda_netlink_License.txt -# [BSD-2-Clause] -# ggc/core/LICENSE/attributions/github_godbus_dbus_License.txt -# ggc/core/LICENSE/attributions/github_huin_gobinarytest_License.txt -# ggc/core/LICENSE/attributions/github_seccomp_libseccomp_golang_License.txt -# ggc/core/LICENSE/attributions/github_syndtr_gocapability_License.txt -# [BSD-3-Clause] -# ggc/core/LICENSE/attributions/github_fsnotify_fsnotify_License.txt -# ggc/core/LICENSE/attributions/github_golang_protobuf_License.txt -# ggc/core/LICENSE/attributions/github_jeffallen_mqtt_License.txt -# ggc/core/LICENSE/attributions/Golang_License.txt -# [ECLIPSE] -# ggc/core/LICENSE/attributions/github_paho_mqtt_License.txt -# [MIT] -# ggc/core/LICENSE/attributions/github_huin_mqtt_License.txt -# ggc/core/LICENSE/attributions/github_mattn_go_sqlite3_License.txt -# ggc/core/LICENSE/attributions/github_nu7hatch_gouuid_License.txt -# ggc/core/LICENSE/attributions/github_Sirupsen_logrus_License.txt -# ggc/core/LICENSE/attributions/github_urfave_cli_License.txt -# [Proprietary] -# ggc/core/LICENSE/Greengrass AWS SW License (IoT additiona) vr6.txt -# [Copyright-Only Decication] -# ggc/core/LICENSE/attributions/libb64_sourceforge_License.txt - -LIC_FILES_CHKSUM = " \ - file://ggc/core/LICENSE/attributions/curl_haxx_se_License.txt;md5=3b77a99e8530d6c9ccc9aab9a7436f99 \ - file://ggc/core/LICENSE/attributions/github_aws_aws_sdk_go_License.txt;md5=d273d63619c9aeaf15cdaf76422c4f87 \ - file://ggc/core/LICENSE/attributions/github_coreos_go_systemd_License.txt;md5=715f3348ed8b9bf4fac3b08133384a4d \ - file://ggc/core/LICENSE/attributions/github_docker_docker_License.txt;md5=bba4ee48af378e39b452d742d29c710b \ - file://ggc/core/LICENSE/attributions/github_docker_go_units_License.txt;md5=bb99db20f1c48c2c4952c27c72855e36 \ - file://ggc/core/LICENSE/attributions/github_fsnotify_fsnotify_License.txt;md5=c38914c9a7ab03bb2b96d4baaee10769 \ - file://ggc/core/LICENSE/attributions/github_godbus_dbus_License.txt;md5=b03a62440372a9acf9692ad365932c87 \ - file://ggc/core/LICENSE/attributions/github_go_ini_ini_License.txt;md5=715f3348ed8b9bf4fac3b08133384a4d \ - file://ggc/core/LICENSE/attributions/github_golang_protobuf_License.txt;md5=16fe162f7848190010b6ec7bfaac030a \ - file://ggc/core/LICENSE/attributions/github_huin_gobinarytest_License.txt;md5=f2b3138d9d314bccf5297dea7e3e6d14 \ - file://ggc/core/LICENSE/attributions/github_huin_mqtt_License.txt;md5=12fd125064676697934b7d8c09bed0e8 \ - file://ggc/core/LICENSE/attributions/github_jeffallen_mqtt_License.txt;md5=b7269d52765d477e10f319c19d8a9d33 \ - file://ggc/core/LICENSE/attributions/github_jmespath_go_jmespath_License.txt;md5=640d33f0070c9dc3a194d2ed7db02974 \ - file://ggc/core/LICENSE/attributions/github_mattn_go_sqlite3_License.txt;md5=948f36a2300ac729e60416063190f664 \ - file://ggc/core/LICENSE/attributions/github_nu7hatch_gouuid_License.txt;md5=6b18748dcc29fda05fa5aaef44d517fd \ - file://ggc/core/LICENSE/attributions/github_opencontainers_runc_License.txt;md5=587c01b2dcc5dc3b4bed51b918c64731 \ - file://ggc/core/LICENSE/attributions/github_opencontainers_runtime_spec_License.txt;md5=ef95ed297310c3d09ba16c06d5e161a5 \ - file://ggc/core/LICENSE/attributions/github_paho_mqtt_License.txt;md5=655315b482a5955efb91d34de332ee48 \ - file://ggc/core/LICENSE/attributions/github_pquerna_ffjson_License.txt;md5=d273d63619c9aeaf15cdaf76422c4f87 \ - file://ggc/core/LICENSE/attributions/github_seccomp_libseccomp_golang_License.txt;md5=9205c4c469bfb9d3a63f346539ee445b \ - file://ggc/core/LICENSE/attributions/github_Sirupsen_logrus_License.txt;md5=29baae91637760ae68feb57ca93e5a0a \ - file://ggc/core/LICENSE/attributions/github_syndtr_gocapability_License.txt;md5=321f58fa53a0b1bb9a887f14660d436b \ - file://ggc/core/LICENSE/attributions/github_urfave_cli_License.txt;md5=f1f14a2449300559aed90bedc36a71ed \ - file://ggc/core/LICENSE/attributions/github_vishvananda_netlink_License.txt;md5=c95fd0efd62139c155e956a448df8fd6 \ - file://ggc/core/LICENSE/attributions/Golang_License.txt;md5=3d7ed06383c65a3161b36c6a0b0b98f5 \ - file://ggc/core/LICENSE/attributions/libb64_sourceforge_License.txt;md5=0f8f14bb84b17652f29aad01072e545f \ - file://ggc/core/LICENSE/attributions/sqlite_org_License.txt;md5=380e2694a297aa32879ca2ae9c6c029b\ -" - -# Bitbake does not support spaces in filenames, but GG License does have spaces, -# so workaround the problem by renaming the file before using it. -GG_LIC_FILENAME = "Greengrass AWS SW License (IoT additional) vr6.txt" -GG_LIC_FILENAME_NOSPACES = "${@d.getVar('GG_LIC_FILENAME').replace(' ','_')}" -LIC_FILES_CHKSUM += "file://ggc/core/LICENSE/${GG_LIC_FILENAME_NOSPACES};md5=7df5bf535d02b2f83c260250fe330b6c" - -SRC_URI[arm.md5sum] = "93ae820af2bf2527bafdb34598d174ed" -SRC_URI[arm.sha256sum] ="8fe99ba17917df2e192b7065e400e2dc85c4a0fbf7654fa0d141642cde92d88f" - -# For ARCH64 we use another tarball. -SRC_URI[aarch64.md5sum] = "e4ec6dba43dcba4d2ec1b04d7c851cd3" -SRC_URI[aarch64.sha256sum] ="f45e502435850fb9a9931a46fd96329f95f53ff2d6d5aaa4bed11094c0237f4c" - -# Rename GG license file -do_unpack[postfuncs] += "rename_license" -rename_license() { - cd ${S}/ggc/core/LICENSE/ - mv "${GG_LIC_FILENAME}" "${GG_LIC_FILENAME_NOSPACES}" -} diff --git a/meta-digi-dey/recipes-aws/greengrass/greengrass_1.7.0.bb b/meta-digi-dey/recipes-aws/greengrass/greengrass_1.7.0.bb deleted file mode 100644 index e8e745e29..000000000 --- a/meta-digi-dey/recipes-aws/greengrass/greengrass_1.7.0.bb +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (C) 2018,2019, Digi International Inc. - -require greengrass.inc - -# -# The Amazon Greengrass Core Product includes the following third-party software/licensing: -# github.com/aws/aws-sdk-go/; version 1.15.65 -- https://github.com/aws/aws-sdk-go/ -# github.com/coreos/go-systemd/; version 10 -- https://github.com/coreos/go-systemd/ -# github.com/docker/docker; version 1.12.0-rc4 -- https://github.com/docker/docker -# github.com/docker/go-units; version 0.3.1 -- https://github.com/docker/go-units -# github.com/go-ini/ini; version 1.32.0 -- https://github.com/go-ini/ini -# github.com/jmespath/go-jmespath; version 0.2.2 -- https://github.com/jmespath/go-jmespath -# github.com/mwitkow/go-http-dialer; version 0.1 -- https://github.com/mwitkow/go-http-dialer -# github.com/opencontainers/runc; version 1.0.0-rc3 -- https://github.com/opencontainers/runc -# github.com/opencontainers/runtime-spec; version 1.0.0-rc5 -- https://github.com/opencontainers/runtime-spec -# github.com/pquerna/ffjson; version 1.0 -- https://github.com/pquerna/ffjson -# github.com/vishvananda/netlink; version 0.1 -- https://github.com/vishvananda/netlink -# -# And the following Licenses: -LIC_FILES_CHKSUM = " \ - file://ggc/core/THIRD-PARTY-LICENSES;md5=28584ceb716d242782f9a7a7593c9ff2 \ -" - -SRC_URI[arm.md5sum] = "a7f3667ac9f24e434e7a85908d1db256" -SRC_URI[arm.sha256sum] ="339656dca947f1cff29635fbe7570b5ea04ca7256fd2177cf396711a60a8f26a" - -# For ARCH64 we use another tarball. -SRC_URI[aarch64.md5sum] = "abfabf1464b7a1da0322dfd780415e48" -SRC_URI[aarch64.sha256sum] ="411956c8a41857c95dea5af6a41c7c0ab09310d621e054693d9e8ee57b23ed35" diff --git a/meta-digi-dey/recipes-aws/greengrass/greengrass_1.8.0.bb b/meta-digi-dey/recipes-aws/greengrass/greengrass_1.8.0.bb deleted file mode 100644 index 9f96f7e5c..000000000 --- a/meta-digi-dey/recipes-aws/greengrass/greengrass_1.8.0.bb +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (C) 2019, Digi International Inc. - -require greengrass.inc - -# -# The Amazon Greengrass Core Product includes the following third-party software/licensing: -# github.com/aws/aws-sdk-go/; version 1.15.65 -- https://github.com/aws/aws-sdk-go/ -# github.com/coreos/go-systemd/; version 10 -- https://github.com/coreos/go-systemd/ -# github.com/docker/docker; version 1.12.0-rc4 -- https://github.com/docker/docker -# github.com/docker/go-units; version 0.3.1 -- https://github.com/docker/go-units -# github.com/go-ini/ini; version 1.32.0 -- https://github.com/go-ini/ini -# github.com/jmespath/go-jmespath; version 0.2.2 -- https://github.com/jmespath/go-jmespath -# github.com/mwitkow/go-http-dialer; version 0.1 -- https://github.com/mwitkow/go-http-dialer -# github.com/opencontainers/runc; version 1.0.0-rc3 -- https://github.com/opencontainers/runc -# github.com/opencontainers/runtime-spec; version 1.0.0-rc5 -- https://github.com/opencontainers/runtime-spec -# github.com/pquerna/ffjson; version 1.0 -- https://github.com/pquerna/ffjson -# github.com/vishvananda/netlink; version 0.1 -- https://github.com/vishvananda/netlink -# -# And the following Licenses: -LIC_FILES_CHKSUM = " \ - file://ggc/core/THIRD-PARTY-LICENSES;md5=70018c9eb1875d260c975eef52c10657 \ -" - -SRC_URI[arm.md5sum] = "41e862deb244c563d438e4604b8b3ccc" -SRC_URI[arm.sha256sum] ="3658af95e21723f52533e441f8e3a9d9e167a8bc4ada6fc957201b6455438961" - -# For ARCH64 we use another tarball. -SRC_URI[aarch64.md5sum] = "4fcd160f685a5131f5aabd6f7cc31b48" -SRC_URI[aarch64.sha256sum] ="8dd7341a51afe03102ea6408a6a529eee9f3a89519fdfcb14e5a9039e711f21b" diff --git a/meta-digi-dey/recipes-aws/greengrass/greengrass_1.9.2.bb b/meta-digi-dey/recipes-aws/greengrass/greengrass_1.9.2.bb deleted file mode 100644 index 5e9afe5d5..000000000 --- a/meta-digi-dey/recipes-aws/greengrass/greengrass_1.9.2.bb +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (C) 2019, Digi International Inc. - -require greengrass.inc - -# -# The Amazon Greengrass Core Product includes the following third-party software/licensing: -# github.com/aws/aws-sdk-go/; version 1.15.65 -- https://github.com/aws/aws-sdk-go/ -# github.com/coreos/go-systemd/; version 10 -- https://github.com/coreos/go-systemd/ -# github.com/docker/docker; version 1.12.0-rc4 -- https://github.com/docker/docker -# github.com/docker/go-units; version 0.3.1 -- https://github.com/docker/go-units -# github.com/go-ini/ini; version 1.32.0 -- https://github.com/go-ini/ini -# github.com/jmespath/go-jmespath; version 0.2.2 -- https://github.com/jmespath/go-jmespath -# github.com/mwitkow/go-http-dialer; version 0.1 -- https://github.com/mwitkow/go-http-dialer -# github.com/opencontainers/runc; version 1.0.0-rc3 -- https://github.com/opencontainers/runc -# github.com/opencontainers/runtime-spec; version 1.0.0-rc5 -- https://github.com/opencontainers/runtime-spec -# github.com/pquerna/ffjson; version 1.0 -- https://github.com/pquerna/ffjson -# github.com/vishvananda/netlink; version 0.1 -- https://github.com/vishvananda/netlink -# -# And the following Licenses: -LIC_FILES_CHKSUM = " \ - file://ggc/core/THIRD-PARTY-LICENSES;md5=53b6a4caa097863bc3971d5e0ac6d1db \ -" - -SRC_URI[arm.md5sum] = "63a1f6aae22260be19f34f278f7e7833" -SRC_URI[arm.sha256sum] ="4bc0bc8a938cdb3d846df92e502155c6ec8cbaf1b63dfa9f3cc3a51372d95af5" - -# For ARCH64 we use another tarball. -SRC_URI[aarch64.md5sum] = "967cd25ac77e733b0a1b42d83dc96ed1" -SRC_URI[aarch64.sha256sum] ="4cbaf91e5354fe49ded160415394413f068426c2bba13089e6b8a28775990a9c" diff --git a/meta-digi-dey/recipes-core/images/dey-image-aws.bb b/meta-digi-dey/recipes-core/images/dey-image-aws.bb deleted file mode 100644 index 6d53eb9f0..000000000 --- a/meta-digi-dey/recipes-core/images/dey-image-aws.bb +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (C) 2017-2023, Digi International Inc. - -DESCRIPTION = "DEY image including Amazon Web Services packages" -LICENSE = "MIT" - -AWS_PACKAGES ?= " \ - awsiotsdk-demo \ - greengrass \ -" - -IMAGE_INSTALL = " \ - packagegroup-dey-core \ - ${AWS_PACKAGES} \ - ${CORE_IMAGE_EXTRA_INSTALL} \ -" - -IMAGE_FEATURES += " \ - dey-network \ - ssh-server-dropbear \ - ${@bb.utils.contains('MACHINE_FEATURES', 'bluetooth', 'dey-bluetooth', '', d)} \ - ${@bb.utils.contains('MACHINE_FEATURES', 'wifi', 'dey-wireless', '', d)} \ -" - -IMAGE_LINGUAS = "" - -inherit core-image -inherit dey-image - -IMAGE_ROOTFS_SIZE = "8192" diff --git a/meta-digi-dey/recipes-core/images/dey-image-tiny-initramfs.bb b/meta-digi-dey/recipes-core/images/dey-image-tiny-initramfs.bb deleted file mode 100644 index ef9680bdb..000000000 --- a/meta-digi-dey/recipes-core/images/dey-image-tiny-initramfs.bb +++ /dev/null @@ -1,11 +0,0 @@ -# -# Copyright (C) 2014, Digi International Inc. -# -DESCRIPTION = "DEY busybox only based initramfs image." - -include dey-image-tiny.bb - -export IMAGE_BASENAME = "dey-image-tiny-initramfs" - -IMAGE_FSTYPES = "cpio.gz.u-boot.tf" -inherit image_types diff --git a/meta-digi-dey/recipes-core/images/dey-image-tiny.bb b/meta-digi-dey/recipes-core/images/dey-image-tiny.bb deleted file mode 100644 index 68b4b423f..000000000 --- a/meta-digi-dey/recipes-core/images/dey-image-tiny.bb +++ /dev/null @@ -1,25 +0,0 @@ -# -# Copyright (C) 2014, Digi International Inc. -# -DESCRIPTION = "DEY busybox only based image." - -IMAGE_INSTALL= "\ - base-files \ - base-passwd \ - busybox \ - sysvinit \ - initscripts \ - ${CORE_IMAGE_EXTRA_INSTALL} \ -" - -IMAGE_LINGUAS = " " - -LICENSE = "MIT" - -inherit core-image -inherit dey-image - -IMAGE_ROOTFS_SIZE ?= "8192" - -IMAGE_FSTYPES:remove = "ext4" -IMAGE_FSTYPES:append = " ext2" diff --git a/meta-digi-dey/recipes-devtools/python3-svgwrite/python3-svgwrite_1.4.3.bb b/meta-digi-dey/recipes-devtools/python3-svgwrite/python3-svgwrite_1.4.3.bb deleted file mode 100644 index 3c98a9423..000000000 --- a/meta-digi-dey/recipes-devtools/python3-svgwrite/python3-svgwrite_1.4.3.bb +++ /dev/null @@ -1,13 +0,0 @@ -DESCRIPTION = "Python library to create SVG drawings" -SECTION = "devel/python" -HOMEPAGE = "https://github.com/mozman/svgwrite" -LICENSE = "MIT" -LIC_FILES_CHKSUM = "file://LICENSE.TXT;md5=3e14f2d1a8674ddcbbd8b51762250049" - -inherit pypi setuptools3 - -PYPI_PACKAGE = "svgwrite" -PYPI_PACKAGE_EXT = "zip" - -SRC_URI[md5sum] = "8e6d536bdffefa03341b77dff5add485" -SRC_URI[sha256sum] = "a8fbdfd4443302a6619a7f76bc937fc683daf2628d9b737c891ec08b8ce524c3" diff --git a/meta-digi-dey/recipes-digi/dey-examples/awsiotsdk-demo_git.bb b/meta-digi-dey/recipes-digi/dey-examples/awsiotsdk-demo_git.bb deleted file mode 100644 index 8dbde22b8..000000000 --- a/meta-digi-dey/recipes-digi/dey-examples/awsiotsdk-demo_git.bb +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (C) 2017-2019, Digi International Inc. - -require recipes-digi/dey-examples/dey-examples-src.inc - -SUMMARY = "AWS IoT device SDK Demo" -DESCRIPTION = "Demo application for AWS IoT device SDK" -SECTION = "base" -LICENSE = "ISC" -LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/ISC;md5=f3b90e78ea0cffb20bf5cca7947a896d" - -DEPENDS = "awsiotsdk-c libconfuse" - -AWS_USER_LED ?= "" -AWS_USER_LED:ccimx6ulstarter ?= "75" -AWS_USER_LED:ccimx6ulsbc ?= "488" -AWS_USER_LED:ccimx6sbc ?= "34" -AWS_USER_LED:ccimx6qpsbc ?= "34" - -S = "${WORKDIR}/git/awsiot-sample" - -inherit aws-iot pkgconfig - -EXTRA_OEMAKE += "'LOG_FLAGS=${@get_log_level(d)}'" - -do_configure() { - # Update the configuration header file. - sed -i -e "s,\(thing_name = \)\"\",\1\"${AWS_IOT_MY_THING_NAME}\",g" \ - -e "s,\(host = \)\"\",\1\"${AWS_IOT_MQTT_HOST}\",g" \ - -e "s,8883,${AWS_IOT_MQTT_PORT},g" \ - -e "s,\([\"']\)rootCA.crt\([\"']\),\1${AWS_IOT_ROOT_CA_FILENAME}\2,g" \ - -e "s,\([\"']\)cert.pem\([\"']\),\1${AWS_IOT_CERTIFICATE_FILENAME}\2,g" \ - -e "s,\([\"']\)privkey.pem\([\"']\),\1${AWS_IOT_PRIVATE_KEY_FILENAME}\2,g" \ - -e "s,\(user_led = \)-1,\1${AWS_USER_LED},g" \ - "${S}/cfg_files/awsiotsdk.conf" -} - -do_install() { - oe_runmake DESTDIR=${D} install -} - -RRECOMMENDS:${PN} += "awsiotsdk-c-cert" - -PACKAGE_ARCH = "${MACHINE_ARCH}" diff --git a/meta-digi-dey/recipes-digi/swu-images/dey-image-aws-swu.bb b/meta-digi-dey/recipes-digi/swu-images/dey-image-aws-swu.bb deleted file mode 100644 index c23692b3b..000000000 --- a/meta-digi-dey/recipes-digi/swu-images/dey-image-aws-swu.bb +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright (C) 2017, Digi International Inc. - -require swu.inc diff --git a/scripts/dey-aws-tool.sh b/scripts/dey-aws-tool.sh deleted file mode 100755 index 0ca1b1756..000000000 --- a/scripts/dey-aws-tool.sh +++ /dev/null @@ -1,141 +0,0 @@ -#!/bin/sh -# -# Copyright (C) 2017 by Digi International Inc. -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, you can obtain one at http://mozilla.org/MPL/2.0/. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# -# Description: AWS CLI wrapper to be used in DEY -# - -SCRIPTNAME="$(basename ${0})" - -USAGE=" -AWS CLI wrapper to facilitate integration with Digi Embedded Yocto - -Usage: ${SCRIPTNAME} [OPTIONS] - - -c, --create-certs Create Root CA and Greengrass Core device certificates under - -t, --thing-name Name of the Greengrass Core Thing. The script will register this Thing in your account if it is not already. -" - -error() { - printf "%s\n" "${1}" - exit 1 -} - -do_create_certs() { - GG_ROOTCA_KEY="${GG_CERTS_DIR}/root-ca.key" - GG_ROOTCA_PEM="${GG_CERTS_DIR}/root-ca.pem" - GG_ROOTCA_VERIF_KEY="${GG_CERTS_DIR}/root-ca-verif.key" - GG_ROOTCA_VERIF_CSR="${GG_CERTS_DIR}/root-ca-verif.csr" - GG_ROOTCA_VERIF_PEM="${GG_CERTS_DIR}/root-ca-verif.pem" - GG_CORE_KEY="${GG_CERTS_DIR}/gg-core.key" - GG_CORE_CSR="${GG_CERTS_DIR}/gg-core.csr" - GG_CORE_PEM="${GG_CERTS_DIR}/gg-core.pem" - - # Verify that no certificate artifact exists in the certs directory - if [ -f "${GG_ROOTCA_KEY}" ] || \ - [ -f "${GG_ROOTCA_PEM}" ] || \ - [ -f "${GG_ROOTCA_PEM}" ] || \ - [ -f "${GG_ROOTCA_VERIF_KEY}" ] || \ - [ -f "${GG_ROOTCA_VERIF_CSR}" ] || \ - [ -f "${GG_ROOTCA_VERIF_PEM}" ] || \ - [ -f "${GG_CORE_KEY}" ] || \ - [ -f "${GG_CORE_CSR}" ] || \ - [ -f "${GG_CORE_PEM}" ]; then - error "[ERROR] Certificates directory contains artifacts from previous execution" - fi - - mkdir -p "${GG_CERTS_DIR}" - - # Get AWS root CA certificate - printf "[INFO] Downloading AWS root CA certificate.\n" - AWS_ROOT_CA_URL="https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem" - [ -f "${GG_CERTS_DIR}/aws-root-ca.pem" ] || wget -t 2 -T 30 -q --passive-ftp --no-check-certificate -O "${GG_CERTS_DIR}/aws-root-ca.pem" "${AWS_ROOT_CA_URL}" - - # - # Create a self-signed CA certificate (if it doesn't exit) - # - printf "[INFO] Creating Greengrass Core CA certificate and key.\n" - GG_ROOTCA_SUBJ="/C=US/ST=Minnesota/L=Minnetonka/O=Digi International Inc./OU=Digi Enginnering/CN=AWS IoT CA Certificate" - openssl genrsa -out "${GG_ROOTCA_KEY}" 2048 2>/dev/null - openssl req -x509 -new -nodes -key "${GG_ROOTCA_KEY}" -sha256 -days 1024 -out "${GG_ROOTCA_PEM}" -subj "${GG_ROOTCA_SUBJ}" - - # - # Create verification certificate (needed to register the CA certificate) - # - printf "[INFO] Creating verification certificate.\n" - if ! REG_CODE="$(aws iot get-registration-code --query registrationCode 2>/dev/null)"; then - error "[ERROR] Unable to get registration code" - fi - GG_ROOTCA_VERIF_SUBJ="/C=US/ST=Minnesota/L=Minnetonka/O=Digi International Inc./OU=Digi Enginnering/CN=${REG_CODE}" - openssl genrsa -out "${GG_ROOTCA_VERIF_KEY}" 2048 2>/dev/null - openssl req -new -key "${GG_ROOTCA_VERIF_KEY}" -out "${GG_ROOTCA_VERIF_CSR}" -subj "${GG_ROOTCA_VERIF_SUBJ}" - openssl x509 -req -in "${GG_ROOTCA_VERIF_CSR}" -CA "${GG_ROOTCA_PEM}" -CAkey "${GG_ROOTCA_KEY}" -CAcreateserial -out "${GG_ROOTCA_VERIF_PEM}" -days 1024 -sha256 2>/dev/null - - # - # Create Greengrass Core device certificate - # - printf "[INFO] Creating Greengrass Core device certificate.\n" - GG_CORE_SUBJ="/C=US/ST=Minnesota/L=Minnetonka/O=Digi International Inc./OU=Digi Enginnering/CN=AWS IoT Device Certificate" - openssl genrsa -out "${GG_CORE_KEY}" 2048 2>/dev/null - openssl req -new -key "${GG_CORE_KEY}" -out "${GG_CORE_CSR}" -subj "${GG_CORE_SUBJ}" - openssl x509 -req -in "${GG_CORE_CSR}" -CA "${GG_ROOTCA_PEM}" -CAkey "${GG_ROOTCA_KEY}" -CAcreateserial -out "${GG_CORE_PEM}" -days 1024 -sha256 2>/dev/null -} - -do_register_thing() { - printf "[INFO] Registering Greengrass Core Thing.\n" - if ! AWS_GGCORE_THING_ARN="$(aws iot create-thing --thing-name ${GG_THING_NAME} --query thingArn 2>/dev/null)"; then - error "[ERROR] Unable to create Greengrass Core Thing" - fi -} - -# Use GNU 'getopt' to parse command line options -SHORT_OPTS="hc:t:" -LONG_OPTS="help,create-certs:,thing-name:" -CMDLINE_OPTS=$(getopt -o ${SHORT_OPTS} -l ${LONG_OPTS} -- "$@") || error "${USAGE}" - -eval set -- "${CMDLINE_OPTS}" - -while true; do - case "$1" in - -h|--help) printf "%s\n" "${USAGE}"; exit 0;; - -c|--create-certs) GG_CERTS_DIR="${2}"; shift;; - -t|--thing-name) GG_THING_NAME="${2}"; shift;; - --) shift; break;; - esac - shift -done - -# Sanity checks: AWS CLI needs to be installed and configured -if ! aws configure get aws_access_key_id >/dev/null 2>&1; then - error "[ERROR] AWS CLI needs to be installed and configured" -fi - -AWS_GGCORE_IOT_HOST="$(aws iot describe-endpoint 2>/dev/null)" - -[ -n "${GG_CERTS_DIR}" ] && do_create_certs -[ -n "${GG_THING_NAME}" ] && do_register_thing - -# Print AWS IoT configuration for DEY projects -cat <<-_EOF_ - - For Greengrass enabled images, add the following configuration to your project: - - AWS_IOT_CERTS_DIR = "${GG_CERTS_DIR:-}" - AWS_GGCORE_IOT_HOST = "${AWS_GGCORE_IOT_HOST:-}" - AWS_GGCORE_THING_ARN = "${AWS_GGCORE_THING_ARN:-}" - - Please verify variables' value is correct. - -_EOF_ From b9bf87d8f1662bfdbd03474066a6897d1ff71a24 Mon Sep 17 00:00:00 2001 From: Gabriel Valcazar Date: Fri, 16 May 2025 09:48:47 +0200 Subject: [PATCH 097/138] packagegroup-qt6-dey: don't install examples in ccmp15-dvk images When building DEY 5.0 ccmp15-dvk images with Qt6 instead of Qt5, the rootfs image is too big for the default settings (512 MiB, dualboot enabled). Make room by removing the *-examples Qt6 packages, which are over 30 MiB in total. Signed-off-by: Gabriel Valcazar --- .../recipes-qt/packagegroups/packagegroup-qt6-dey.bb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/meta-digi-dey/dynamic-layers/qt6-layer/recipes-qt/packagegroups/packagegroup-qt6-dey.bb b/meta-digi-dey/dynamic-layers/qt6-layer/recipes-qt/packagegroups/packagegroup-qt6-dey.bb index 8c6b80cc7..dae243a25 100644 --- a/meta-digi-dey/dynamic-layers/qt6-layer/recipes-qt/packagegroups/packagegroup-qt6-dey.bb +++ b/meta-digi-dey/dynamic-layers/qt6-layer/recipes-qt/packagegroups/packagegroup-qt6-dey.bb @@ -27,9 +27,13 @@ QT6_IMAGE_INSTALL_CINEMATICEXPERIENCE:ccmp15 = "cinematicexperience-rhi cinemati QT6_IMAGE_INSTALL_CINEMATICEXPERIENCE:ccmp25 = "cinematicexperience-rhi cinematicexperience-rhi-tools" QT6_IMAGE_INSTALL_EXAMPLES = "qtbase-examples qtdeclarative-examples" +# Make room in ccmp15-dvk Qt6 images by removing examples +QT6_IMAGE_INSTALL_EXAMPLES:ccmp15 = "" QT6_IMAGE_INSTALL_FONTS = "ttf-dejavu-common ttf-dejavu-sans ttf-dejavu-sans-mono ttf-dejavu-serif " QT6_IMAGE_INSTALL_QUICK3D = "qtquick3d qtquick3d-examples" # Quick3d does not work on systems without a proper GPU QT6_IMAGE_INSTALL_QUICK3D:ccimx93 = "" +# Make room in ccmp15-dvk Qt6 images by removing examples +QT6_IMAGE_INSTALL_QUICK3D:ccmp15 = "qtquick3d" From e60dbe03cbac65e2e326e79358ba2224fce37947 Mon Sep 17 00:00:00 2001 From: Gabriel Valcazar Date: Fri, 16 May 2025 12:20:40 +0200 Subject: [PATCH 098/138] ccmp15-dvk: bblayers: replace meta-qt5 with meta-qt6 Use Qt6 by default in ccmp15-dvk builds Signed-off-by: Gabriel Valcazar --- meta-digi-arm/conf/templates/ccmp15-dvk/bblayers.conf.sample | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meta-digi-arm/conf/templates/ccmp15-dvk/bblayers.conf.sample b/meta-digi-arm/conf/templates/ccmp15-dvk/bblayers.conf.sample index 85195c965..bdbe740ee 100644 --- a/meta-digi-arm/conf/templates/ccmp15-dvk/bblayers.conf.sample +++ b/meta-digi-arm/conf/templates/ccmp15-dvk/bblayers.conf.sample @@ -14,7 +14,7 @@ BBLAYERS ?= " \ ##DIGIBASE##/meta-openembedded/meta-multimedia \ ##DIGIBASE##/meta-openembedded/meta-networking \ ##DIGIBASE##/meta-openembedded/meta-webserver \ - ##DIGIBASE##/meta-qt5 \ + ##DIGIBASE##/meta-qt6 \ ##DIGIBASE##/meta-swupdate \ ##DIGIBASE##/meta-webkit \ ##DIGIBASE##/meta-timesys \ From e03ef8941c4fa5320ab5b149b65e11ffeabc10d4 Mon Sep 17 00:00:00 2001 From: Gabriel Valcazar Date: Fri, 16 May 2025 12:25:37 +0200 Subject: [PATCH 099/138] ccmp1: remove spurious UBI/UBIFS images In theory, we already had the necessary changes to remove these images, but two things needed tweaking: * MULTIUBI_BUILD values use underscores instead of hyphens, so use underscores to properly remove the default value inherited from BOOTDEVICE_LABELS. * STM used to incorporate a custom "stmultiubi" image type in the stm32mp builds, but they've replaced this with the upstream "multiubi" type. Reflect this change to avoid generating additional UBI/UBIFS images in our builds. Signed-off-by: Gabriel Valcazar --- meta-digi-arm/conf/machine/include/ccmp1.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meta-digi-arm/conf/machine/include/ccmp1.inc b/meta-digi-arm/conf/machine/include/ccmp1.inc index 0a58be9f2..46450e26c 100644 --- a/meta-digi-arm/conf/machine/include/ccmp1.inc +++ b/meta-digi-arm/conf/machine/include/ccmp1.inc @@ -85,8 +85,8 @@ BOOTDEVICE_LABELS ?= "nand-4-256-1024 usb" # Remove NAND-based ubi/ubifs images added by st-machine-common-stm32mp.inc # because image_types_digi.bbclass already builds their own. -MULTIUBI_BUILD:remove = "nand-4-256-1024" -IMAGE_FSTYPES:remove = "stmultiubi" +MULTIUBI_BUILD:remove = "nand_4_256_1024" +IMAGE_FSTYPES:remove = "multiubi" # Wic files WKS_FILE_DEPENDS ?= " \ From 119292e68cdacbab33f11f52283ced6d78a8b42c Mon Sep 17 00:00:00 2001 From: Gabriel Valcazar Date: Fri, 16 May 2025 14:32:44 +0200 Subject: [PATCH 100/138] dey-image-flutter: add recipe for dey-image-flutter-swu Like we do for our other supported images, provide a recipe to generate a SWU package based on dey-image-flutter Signed-off-by: Gabriel Valcazar --- meta-digi-dey/recipes-digi/swu-images/dey-image-flutter-swu.bb | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 meta-digi-dey/recipes-digi/swu-images/dey-image-flutter-swu.bb diff --git a/meta-digi-dey/recipes-digi/swu-images/dey-image-flutter-swu.bb b/meta-digi-dey/recipes-digi/swu-images/dey-image-flutter-swu.bb new file mode 100644 index 000000000..198e1695e --- /dev/null +++ b/meta-digi-dey/recipes-digi/swu-images/dey-image-flutter-swu.bb @@ -0,0 +1,3 @@ +# Copyright (C) 2025, Digi International Inc. + +require swu.inc From 5bdd59a6473257bbbe69f2b81fd70ba4fa26dcfd Mon Sep 17 00:00:00 2001 From: Gabriel Valcazar Date: Fri, 9 May 2025 14:16:01 +0200 Subject: [PATCH 101/138] swupdate: fix regression in 2024.12.1 This version of swupdate has a bug that happens if the root of sw-description is redirected via a link, which is the case is some of our sw-description templates (such as the one we use for file updates). Backport a fix from v2025.05. https://onedigi.atlassian.net/browse/ADK4A-1957 Signed-off-by: Gabriel Valcazar --- ...t_find_path-properly-return-to-free-.patch | 170 ++++++++++++++++++ .../swupdate/swupdate_2024.12.bbappend | 5 + 2 files changed, 175 insertions(+) create mode 100644 meta-digi-dey/recipes-support/swupdate/swupdate/0004-Revert-parser-set_find_path-properly-return-to-free-.patch create mode 100644 meta-digi-dey/recipes-support/swupdate/swupdate_2024.12.bbappend diff --git a/meta-digi-dey/recipes-support/swupdate/swupdate/0004-Revert-parser-set_find_path-properly-return-to-free-.patch b/meta-digi-dey/recipes-support/swupdate/swupdate/0004-Revert-parser-set_find_path-properly-return-to-free-.patch new file mode 100644 index 000000000..222684302 --- /dev/null +++ b/meta-digi-dey/recipes-support/swupdate/swupdate/0004-Revert-parser-set_find_path-properly-return-to-free-.patch @@ -0,0 +1,170 @@ +From: Stefano Babic +Date: Fri, 28 Feb 2025 17:19:05 +0100 +Subject: [PATCH] Revert "parser: set_find_path: properly return to-free string + array" + +This reverts commit cefc5ae2e43c78697281c529a29b61c4bdb3c037. + +The commit causes a regression bug by following links in the +sw-description if the root of the tree is redirected via a link. An +example to create the issue is: + + stable : { + copy1: { + partitions: ( + .......... + ); + images: ( + .......... + ); + scripts: ( + .......... + ); + }; + + copy2: { + ref = "#./copy1"; + }; + }; + +An update with -e stable,copy1 works, while -e stable,copy2 does not +find anything to be installed because the link is not followed. + +Signed-off-by: Stefano Babic +Reported-by: Konstantin Shabalovski +Reported-by: Florian Amstutz +(cherry picked from commit 4294da8b720ac9bbf25001fddb0ee63d39207dc1) + +parser: fix issues related to commit cefc5ae2 + +The commit above fixes a memory leak, but on a structure that was mainly +used for debugging and points to used node array in case of links. If +this structure is freed, links recursion does not work anymore. + +After reverting the commit above, remove the structure causing the +regression bug because it is not really used. + +Signed-off-by: Stefano Babic +(cherry picked from commit 93fd657cd2fd4a396cd903bfcd576f6475d1dcdf) +--- + core/parsing_library.c | 4 ++-- + corelib/parsing_library_libconfig.c | 4 +--- + corelib/parsing_library_libjson.c | 4 +--- + include/parselib.h | 2 +- + parser/parser.c | 4 +--- + 5 files changed, 6 insertions(+), 12 deletions(-) + +diff --git a/core/parsing_library.c b/core/parsing_library.c +index 1dc0c42a..a1fcfdd8 100644 +--- a/core/parsing_library.c ++++ b/core/parsing_library.c +@@ -239,7 +239,7 @@ void get_hash_value(parsertype p, void *elem, unsigned char *hash) + ascii_to_hash(hash, hash_ascii); + } + +-bool set_find_path(const char **nodes, const char *newpath, char ***tmp) ++bool set_find_path(const char **nodes, const char *newpath) + { + char **paths; + unsigned int count; +@@ -329,7 +329,7 @@ bool set_find_path(const char **nodes, const char *newpath, char ***tmp) + } + + free(ref); +- *tmp = paths; ++ free(paths); + + return true; + } +diff --git a/corelib/parsing_library_libconfig.c b/corelib/parsing_library_libconfig.c +index 35ae903e..bcb72a11 100644 +--- a/corelib/parsing_library_libconfig.c ++++ b/corelib/parsing_library_libconfig.c +@@ -182,7 +182,6 @@ void *find_root_libconfig(config_t *cfg, const char **nodes, unsigned int depth) + config_setting_t *elem; + char *root; + const char *ref; +- char **tmp = NULL; + + /* + * check for deadlock links, block recursion +@@ -204,12 +203,11 @@ void *find_root_libconfig(config_t *cfg, const char **nodes, unsigned int depth) + if (elem && config_setting_is_group(elem) == CONFIG_TRUE) { + ref = get_field_string_libconfig(elem, "ref"); + if (ref) { +- if (!set_find_path(nodes, ref, &tmp)) { ++ if (!set_find_path(nodes, ref)) { + free(root); + return NULL; + } + elem = find_root_libconfig(cfg, nodes, depth); +- free_string_array(tmp); + } + } + +diff --git a/corelib/parsing_library_libjson.c b/corelib/parsing_library_libjson.c +index e78ed000..99b60f55 100644 +--- a/corelib/parsing_library_libjson.c ++++ b/corelib/parsing_library_libjson.c +@@ -220,7 +220,6 @@ void *find_root_json(json_object *root, const char **nodes, unsigned int depth) + { + json_object *node; + enum json_type type; +- char **tmp = NULL; + const char *str; + + /* +@@ -237,10 +236,9 @@ void *find_root_json(json_object *root, const char **nodes, unsigned int depth) + if (type == json_type_object || type == json_type_array) { + str = get_field_string_json(node, "ref"); + if (str) { +- if (!set_find_path(nodes, str, &tmp)) ++ if (!set_find_path(nodes, str)) + return NULL; + node = find_root_json(root, nodes, depth); +- free_string_array(tmp); + } + } + } +diff --git a/include/parselib.h b/include/parselib.h +index 2837dc33..40e98f68 100644 +--- a/include/parselib.h ++++ b/include/parselib.h +@@ -63,7 +63,7 @@ void get_hash_value(parsertype p, void *elem, unsigned char *hash); + void check_field_string(const char *src, char *dst, const size_t max_len); + void *find_root(parsertype p, void *root, const char **nodes); + void *get_node(parsertype p, void *root, const char **nodes); +-bool set_find_path(const char **nodes, const char *newpath, char ***tmp); ++bool set_find_path(const char **nodes, const char *newpath); + + static inline void get_field_bool(parsertype p, void *e, const char *path, bool *dest) + { +diff --git a/parser/parser.c b/parser/parser.c +index 99c3396e..a8f4a691 100644 +--- a/parser/parser.c ++++ b/parser/parser.c +@@ -132,7 +132,6 @@ static int parser_follow_link(parsertype p, void *cfg, void *elem, + { + const char *ref; + void *link; +- char **tmp = NULL; /* to store temporary link path */ + const char **linknodes; + int result = 0; + +@@ -144,7 +143,7 @@ static int parser_follow_link(parsertype p, void *cfg, void *elem, + for (int j = 0; j < count_string_array(nodes); j++) { + linknodes[j] = nodes[j]; + } +- if (!set_find_path(linknodes, ref, &tmp)) { ++ if (!set_find_path(linknodes, ref)) { + free(linknodes); + return -1; + } +@@ -153,7 +152,6 @@ static int parser_follow_link(parsertype p, void *cfg, void *elem, + if (link) { + result = fn(p, cfg, link, linknodes, swcfg, L); + } +- free_string_array(tmp); + free(linknodes); + return result; + } diff --git a/meta-digi-dey/recipes-support/swupdate/swupdate_2024.12.bbappend b/meta-digi-dey/recipes-support/swupdate/swupdate_2024.12.bbappend new file mode 100644 index 000000000..a7f99ed9e --- /dev/null +++ b/meta-digi-dey/recipes-support/swupdate/swupdate_2024.12.bbappend @@ -0,0 +1,5 @@ +# Copyright (C) 2025, Digi International Inc. + +SRC_URI += " \ + file://0004-Revert-parser-set_find_path-properly-return-to-free-.patch \ +" From 3eb66c25cb58111dd925854f4d808c9e02ad042a Mon Sep 17 00:00:00 2001 From: Gabriel Valcazar Date: Tue, 20 May 2025 12:15:03 +0200 Subject: [PATCH 102/138] packagegroup-dey-examples: remove outdated dependency with dey-examples-v4l2 Commit 15f9aeba0eed66443c56925c9c99c3b2df851aac removed the dey-examples-v4l2 recipe, yet we still kept it as a dependency for ccimx6 platforms. Remove this impossible dependency to be able to build the examples packagegroup for ccimx6 platforms. Signed-off-by: Gabriel Valcazar --- .../recipes-digi/packagegroups/packagegroup-dey-examples.bb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/meta-digi-dey/recipes-digi/packagegroups/packagegroup-dey-examples.bb b/meta-digi-dey/recipes-digi/packagegroups/packagegroup-dey-examples.bb index f394744d5..30e8ee250 100644 --- a/meta-digi-dey/recipes-digi/packagegroups/packagegroup-dey-examples.bb +++ b/meta-digi-dey/recipes-digi/packagegroups/packagegroup-dey-examples.bb @@ -1,4 +1,4 @@ -# Copyright (C) 2013-2024, Digi International Inc. +# Copyright (C) 2013-2025, Digi International Inc. SUMMARY = "DEY examples packagegroup" @@ -24,7 +24,6 @@ RDEPENDS:${PN} = "\ " RDEPENDS:${PN}:append:ccimx6 = "\ ${@bb.utils.contains("MACHINE_FEATURES", "accel-graphics", "dey-examples-opengles", "", d)} \ - dey-examples-v4l2 \ " COMPATIBLE_MACHINE = "(ccimx6$|ccimx6ul|ccimx8m|ccimx8x|ccimx9)" From 0d9762442efe5b59c9ebdd3566dd6a3c62bf4a15 Mon Sep 17 00:00:00 2001 From: Gabriel Valcazar Date: Tue, 20 May 2025 12:53:30 +0200 Subject: [PATCH 103/138] dey-examples: set correct COMPATIBLE_MACHINE where applicable Some of our example recipes that are meant to be used on specific platform subsets are available to be built/installed for all platforms. Even though it's possible to build/install these recipes for platforms they aren't meant for, we should restrict them to their intended platforms: * dey-examples-btconfig: this is only meant for ccimx6 platforms that use the Atheros wifi/bt chip, so reflect this in the recipe and in the examples packagegroup. * dey-examples-tamper: this is only meant for platforms with MCA (ccimx6ul, ccimx8x and ccimx8m). This requirement was already set in the examples packagegroup, but the recipe was available to any platform. https://onedigi.atlassian.net/browse/DEL-9612 Signed-off-by: Gabriel Valcazar --- .../recipes-digi/dey-examples/dey-examples-btconfig.bb | 4 +++- .../recipes-digi/dey-examples/dey-examples-tamper.bb | 4 +++- .../recipes-digi/packagegroups/packagegroup-dey-examples.bb | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/meta-digi-dey/recipes-digi/dey-examples/dey-examples-btconfig.bb b/meta-digi-dey/recipes-digi/dey-examples/dey-examples-btconfig.bb index 16e4cd96b..518d06e19 100644 --- a/meta-digi-dey/recipes-digi/dey-examples/dey-examples-btconfig.bb +++ b/meta-digi-dey/recipes-digi/dey-examples/dey-examples-btconfig.bb @@ -1,4 +1,4 @@ -# Copyright (C) 2013-2022, Digi International Inc. +# Copyright (C) 2013-2025, Digi International Inc. SUMMARY = "DEY examples: application to perform low level bluetooth" SECTION = "examples" @@ -19,3 +19,5 @@ do_install() { install -d ${D}${bindir} install -m 0755 btconfig ${D}${bindir} } + +COMPATIBLE_MACHINE = "(ccimx6$)" diff --git a/meta-digi-dey/recipes-digi/dey-examples/dey-examples-tamper.bb b/meta-digi-dey/recipes-digi/dey-examples/dey-examples-tamper.bb index 1e8ba262f..4bb1436da 100644 --- a/meta-digi-dey/recipes-digi/dey-examples/dey-examples-tamper.bb +++ b/meta-digi-dey/recipes-digi/dey-examples/dey-examples-tamper.bb @@ -1,4 +1,4 @@ -# Copyright (C) 2022, Digi International Inc. +# Copyright (C) 2022-2025, Digi International Inc. SUMMARY = "DEY examples: Tamper test application" SECTION = "examples" @@ -13,3 +13,5 @@ do_install() { install -d ${D}${bindir} install -m 0755 tamper_sample ${D}${bindir} } + +COMPATIBLE_MACHINE = "(ccimx6ul|ccimx8m|ccimx8x)" diff --git a/meta-digi-dey/recipes-digi/packagegroups/packagegroup-dey-examples.bb b/meta-digi-dey/recipes-digi/packagegroups/packagegroup-dey-examples.bb index 30e8ee250..b0503067b 100644 --- a/meta-digi-dey/recipes-digi/packagegroups/packagegroup-dey-examples.bb +++ b/meta-digi-dey/recipes-digi/packagegroups/packagegroup-dey-examples.bb @@ -10,7 +10,6 @@ inherit packagegroup RDEPENDS:${PN} = "\ ${@bb.utils.contains("MACHINE_FEATURES", "alsa", "dey-examples-alsa", "", d)} \ ${@bb.utils.contains("MACHINE_FEATURES", "bluetooth", "dey-examples-bt", "", d)} \ - ${@bb.utils.contains("MACHINE_FEATURES", "bluetooth", "dey-examples-btconfig", "", d)} \ ${@bb.utils.contains("MACHINE_FEATURES", "bluetooth", "dey-examples-bt-gatt-server", "", d)} \ ${@bb.utils.contains("MACHINE_FEATURES", "bluetooth", "dey-examples-hdp", "", d)} \ ${@bb.utils.contains("MACHINE_FEATURES", "cryptochip", "dey-examples-cryptochip", "", d)} \ @@ -24,6 +23,7 @@ RDEPENDS:${PN} = "\ " RDEPENDS:${PN}:append:ccimx6 = "\ ${@bb.utils.contains("MACHINE_FEATURES", "accel-graphics", "dey-examples-opengles", "", d)} \ + ${@bb.utils.contains("MACHINE_FEATURES", "bluetooth", "dey-examples-btconfig", "", d)} \ " COMPATIBLE_MACHINE = "(ccimx6$|ccimx6ul|ccimx8m|ccimx8x|ccimx9)" From 6345d46ce1bba1f937b61f1a15dc61a1f22fc53d Mon Sep 17 00:00:00 2001 From: Gabriel Valcazar Date: Tue, 13 May 2025 13:19:47 +0200 Subject: [PATCH 104/138] bluez5: remove btconfig binary in ccimx6 platforms poky commit 3c9b461dd0d26a5f6941000d839636ad32cd6f29 added this binary to the bluez5-noinst-tools package, and it has the same exact path as the one from our dey-examples-btconfig package. This causes a conflict when both packages are installed in the same image. Judging by bluez5's btconfig sources, it's merely a skeleton without any real functionality, so simply remove it to avoid the conflict. https://onedigi.atlassian.net/browse/DEL-9612 Signed-off-by: Gabriel Valcazar --- .../recipes-connectivity/bluez/bluez5_5.72.bbappend | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/meta-digi-dey/recipes-connectivity/bluez/bluez5_5.72.bbappend b/meta-digi-dey/recipes-connectivity/bluez/bluez5_5.72.bbappend index 5a9a085d7..ef5e3e391 100644 --- a/meta-digi-dey/recipes-connectivity/bluez/bluez5_5.72.bbappend +++ b/meta-digi-dey/recipes-connectivity/bluez/bluez5_5.72.bbappend @@ -1,4 +1,4 @@ -# Copyright (C) 2015-2024, Digi International Inc. +# Copyright (C) 2015-2025, Digi International Inc. FILESEXTRAPATHS:prepend := "${THISDIR}/${BPN}:${THISDIR}/${BP}:" @@ -72,4 +72,8 @@ PACKAGECONFIG:append = " health-profiles \ FILES:${PN} += " ${sysconfdir}/bluetooth/main.conf*" +# bluez5's btconfig binary conflicts with dey-examples-btconfig. The tool +# doesn't seem to do much anyway, so just remove it. +NOINST_TOOLS_BT:remove:ccimx6 = "tools/btconfig" + PACKAGE_ARCH = "${MACHINE_ARCH}" From fdadc10e303515a371af9dedf446f155eef4daf6 Mon Sep 17 00:00:00 2001 From: Isaac Hermida Date: Tue, 20 May 2025 12:42:36 +0200 Subject: [PATCH 105/138] recipes-digi: nxp-wlan-utils: add nxp wlan tools add a new recipe to include nxp prebuilt binaries for: * mlanutl: WoWLAN support * nanapp: NAN support * nmlawls: monitor Wi-Fi events Included prebuilt version obtained from next release: SD-WLAN-UART-BT-SPI-OT-Zigbee-DualPAN-IW612-LNX_6_12_3-IMX8-18.99.3.p23.20-18.99.3.p23.20-MM6X18505.p23-GPL.zip https://onedigi.atlassian.net/browse/DEL-8462 Signed-off-by: Isaac Hermida --- .../packagegroup-dey-wireless.bb | 3 ++- .../recipes-digi/nxp-wlan-utils/files/mlanutl | Bin 0 -> 423792 bytes .../recipes-digi/nxp-wlan-utils/files/mlanwls | Bin 0 -> 338408 bytes .../recipes-digi/nxp-wlan-utils/files/nanapp | Bin 0 -> 1538416 bytes .../nxp-wlan-utils/nxp-wlan-utils.bb | 23 ++++++++++++++++++ 5 files changed, 25 insertions(+), 1 deletion(-) create mode 100755 meta-digi-dey/recipes-digi/nxp-wlan-utils/files/mlanutl create mode 100755 meta-digi-dey/recipes-digi/nxp-wlan-utils/files/mlanwls create mode 100755 meta-digi-dey/recipes-digi/nxp-wlan-utils/files/nanapp create mode 100644 meta-digi-dey/recipes-digi/nxp-wlan-utils/nxp-wlan-utils.bb diff --git a/meta-digi-dey/recipes-connectivity/packagegroups/packagegroup-dey-wireless.bb b/meta-digi-dey/recipes-connectivity/packagegroups/packagegroup-dey-wireless.bb index 8a7715218..3da334aa4 100644 --- a/meta-digi-dey/recipes-connectivity/packagegroups/packagegroup-dey-wireless.bb +++ b/meta-digi-dey/recipes-connectivity/packagegroups/packagegroup-dey-wireless.bb @@ -1,5 +1,5 @@ # -# Copyright (C) 2012-2021, Digi International Inc. +# Copyright (C) 2012-2025, Digi International Inc. # SUMMARY = "Wireless packagegroup for DEY image" @@ -16,3 +16,4 @@ RDEPENDS:${PN} = "\ " RDEPENDS:${PN}:append:ccimx6sbc = " ath-prop-tools" +RDEPENDS:${PN}:append:ccimx9 = " nxp-wlan-utils" diff --git a/meta-digi-dey/recipes-digi/nxp-wlan-utils/files/mlanutl b/meta-digi-dey/recipes-digi/nxp-wlan-utils/files/mlanutl new file mode 100755 index 0000000000000000000000000000000000000000..f2714b19246aa4609cdcb5d79a38c5b9d582382b GIT binary patch literal 423792 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 Z!KXZZ@d2+Ce@HG8!t=4R2xX}Ee*kyd)*=7^ literal 0 HcmV?d00001 diff --git a/meta-digi-dey/recipes-digi/nxp-wlan-utils/files/mlanwls b/meta-digi-dey/recipes-digi/nxp-wlan-utils/files/mlanwls new file mode 100755 index 0000000000000000000000000000000000000000..12579dd831e85727c4c9d2517f2d96146493bea2 GIT binary patch literal 338408 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 XV%|}E4`jz9ll3piYMr&f3?g>`PtQs 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 z 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%0A(sG64Cy`uu&ddu6=S-ed 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 Fi16>|sP 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%0DLRwX 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 Date: Fri, 16 May 2025 10:37:28 +0200 Subject: [PATCH 106/138] trustfence-fscrypt: add support to STM32MP1x platforms based on libteec v1.0.0 This commit includes a new tarball based on trustfence-fscrypt v0.2, cross-compiled against libteec v1.0.0, to enable support for STM32MP1x platforms. Signed-off-by: Arturo Buzarra --- meta-digi-dey/recipes-core/trustfence/trustfence-fscrypt_0.2.bb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/meta-digi-dey/recipes-core/trustfence/trustfence-fscrypt_0.2.bb b/meta-digi-dey/recipes-core/trustfence/trustfence-fscrypt_0.2.bb index 5d957ca9d..61003c2b9 100644 --- a/meta-digi-dey/recipes-core/trustfence/trustfence-fscrypt_0.2.bb +++ b/meta-digi-dey/recipes-core/trustfence/trustfence-fscrypt_0.2.bb @@ -17,6 +17,8 @@ SRC_URI[arm.sha256sum] = "7dee4bbcff21d817bbbc152e904e8091362378446b08ad2d485f37 SRC_URI:stm32mpcommon = "${DIGI_PKG_SRC}/${BP}-${TF_FSCRYPT_ARCH}-libteec-v1.0.0.tar.gz;name=${TARGET_ARCH}-libteecv1" SRC_URI[aarch64-libteecv1.md5sum] = "67c2d87312ccae750de8165e82691c63" SRC_URI[aarch64-libteecv1.sha256sum] = "43c2e900ca8d0aaac15963ffb5a7c57e3dd07613b2fe236955012039027b03f4" +SRC_URI[arm-libteecv1.md5sum] = "6b153a51a4c3b77d8172ce37c6542c59" +SRC_URI[arm-libteecv1.sha256sum] = "bc65a13d234da8d4a9c0cfd6d0a8672e8fe1c1c884180f47121d41bd7dcefafe" # Needed to resolve dependencies to libteec RDEPENDS:${PN} += "optee-client" From 09714323a623829df403d8b3eee18826eb0c4549 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Fri, 23 May 2025 08:57:52 +0200 Subject: [PATCH 107/138] ccmp1: adjust memory layout for FIT image support This commit updates the memory layout to properly allocate space for the different FIT image components, while ensuring total memory usage stays below 128 MiB. This avoids overlaps and ensures correct loading on memory-constrained variants. Final memory map: Start of memory: # 0xC0000000 # | # | 32 MiB reserved # v FIT image load address: # 0xC2000000 # | # | 32 MiB for FIT image # v Kernel load address: # 0xC4000000 # | # | 32 MiB for Kernel # v DTB/DTBO load address: # 0xC6000000 # | # | Size for DTB/DTBO # v Total memory mapped: 96 MiB https://onedigi.atlassian.net/browse/DEL-9634 Signed-off-by: Arturo Buzarra --- meta-digi-arm/conf/machine/include/ccmp1.inc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/meta-digi-arm/conf/machine/include/ccmp1.inc b/meta-digi-arm/conf/machine/include/ccmp1.inc index 46450e26c..9260f2a37 100644 --- a/meta-digi-arm/conf/machine/include/ccmp1.inc +++ b/meta-digi-arm/conf/machine/include/ccmp1.inc @@ -22,8 +22,9 @@ FIP_UBOOT_DTB = "u-boot" # Include boot script into the FIT image UBOOT_ENV = "boot" # For FIT image usage -UBOOT_DTB_LOADADDRESS = "0xc4000000" -UBOOT_DTBO_LOADADDRESS = "0xc4000000" +UBOOT_ENTRYPOINT = "0xc4000000" +UBOOT_DTB_LOADADDRESS = "0xc6000000" +UBOOT_DTBO_LOADADDRESS = "0xc6000000" # Set InitRAMDisk load address to U-boot initrd_addr UBOOT_RD_LOADADDRESS = "0xc4400000" # U-Boot environment offset (within partition) From 8644348fed0faaf30ec532b2b3093e6db96b847b Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Fri, 23 May 2025 09:20:42 +0200 Subject: [PATCH 108/138] stm-st-stm32mp: optee-os: remove CFG_OTP_HUK for Trustfence Starting with OP-TEE v4.0.0, the use of a test key is no longer supported. The Hardware Unique Key (HUK) is now always derived from the programmed OTP bits. As a result, the Digi custom `CFG_OTP_HUK` flag is obsolete and has been removed. https://onedigi.atlassian.net/browse/DEL-9634 Signed-off-by: Arturo Buzarra --- .../recipes-security/optee/optee-os-stm32mp_4.0.0.bbappend | 3 --- 1 file changed, 3 deletions(-) diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-os-stm32mp_4.0.0.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-os-stm32mp_4.0.0.bbappend index c57ce1a4a..1d508f1df 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-os-stm32mp_4.0.0.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-os-stm32mp_4.0.0.bbappend @@ -17,6 +17,3 @@ SRC_URI = " \ ${OPTEE_GIT_URI};branch=${SRCBRANCH};name=os \ file://fonts.tar.gz;subdir=git;name=fonts \ " - -# If TF enabled, force use of HUK in OTP bits -EXTRA_OEMAKE += "${@oe.utils.conditional('TRUSTFENCE_ENABLED', '1', 'CFG_OTP_HUK=1', '', d)}" From 11c34bcbdb9e0d9ac18c91a07a57e6bcf050a178 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Fri, 23 May 2025 11:10:23 +0200 Subject: [PATCH 109/138] u-boot-dey: ccmp1/ccmp2: fix support for inserting signed keys into U-Boot DTB for FIT images Since commit 29d32063ac0abb1017756f62f94aec22ce305b60 ("u-boot: kernel-fitimage: Fix dependency loop if UBOOT_SIGN_ENABLE and UBOOT_ENV enabled") in Poky layer, the `kernel-fitimage` and `uboot-sign` classes are no longer explicitly dependent. This change introduced a race condition when inserting the signed RSA keys into the U-Boot DTB for FIT image verification. This commit introduces a new step for `do_uboot_assemble_fitimage`, which is now responsible for injecting the keys into the U-Boot DTB. This logic was previously handled in the Linux kernel recipe via the `do_assemble_fitimage` function in previous Yocto versions. Additionally, a build-time dependency is added between the `do_uboot_assemble_fitimage()` function and the kernel's `do_kernel_generate_rsa_keys()` task, which is responsible for generating the RSA keys used to sign the FIT image. https://onedigi.atlassian.net/browse/DEL-9634 Signed-off-by: Arturo Buzarra --- .../recipes-bsp/u-boot/u-boot-dey.inc | 62 +++++++++++++------ 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc index 4fc8bd759..7c9291d50 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024, Digi International Inc. +# Copyright (C) 2018-2025, Digi International Inc. require recipes-bsp/u-boot/u-boot.inc @@ -218,7 +218,7 @@ do_compile:append:ccmp1() { } BOOT_TOOLS = "imx-boot-tools" -BOOT_TOOLS:ccmp1 = "u-boot" +BOOT_TOOLS:stm32mpcommon = "u-boot" do_deploy:append:ccimx8m() { # Deploy u-boot-nodtb.bin and ccimx8m[m|n]-dvk.dtb, to be packaged in boot binary by imx-boot @@ -241,33 +241,59 @@ do_deploy:append:ccimx8m() { fi } -do_deploy:append:ccmp1() { +do_deploy:append:stm32mpcommon() { # Deploy u-boot-nodtb.bin and ccmp1x-dvk-xxxx.dtb, to be packaged in fip binary by tf-a install -d ${DEPLOYDIR}/${BOOT_TOOLS} if [ -n "${UBOOT_DEVICETREE}" ]; then for devicetree in ${UBOOT_DEVICETREE}; do # Install u-boot dtb install -m 644 ${B}/${config}/arch/arm/dts/${devicetree}.dtb ${DEPLOYDIR}/${BOOT_TOOLS}/${FIP_UBOOT_DTB}-${devicetree}.dtb + + if [ "${UBOOT_SIGN_ENABLE}" = "1" ]; then + # Keep u-boot devicetree without signature + ubootdevicetree="${B}/${config}/arch/arm/dts/${devicetree}.dtb" + namewithoutsignature=`echo $ubootdevicetree | sed "s/\.dtb/-without-signature.dtb/g"` + # Install unsigned U-Boot dtb + install -m 644 ${namewithoutsignature} ${DEPLOYDIR}/${BOOT_TOOLS}/${FIP_UBOOT_DTB}-${devicetree}-without-signature.dtb + fi done fi install -m 0777 ${B}/${config}/u-boot-nodtb.bin ${DEPLOYDIR}/${BOOT_TOOLS}/u-boot-nodtb.bin - - # Append signature to u-boot DT - if [ "x${UBOOT_SIGN_ENABLE}" = "x1" ] && [ -n "${UBOOT_DEVICETREE}" ] ; then - for devicetree in ${UBOOT_DEVICETREE}; do - # get name of u-boot devicetree without signature - ubootdevicetree="${DEPLOYDIR}/${BOOT_TOOLS}/${FIP_UBOOT_DTB}-${devicetree}.dtb" - namewithoutsignature=`echo $ubootdevicetree | sed "s/\.dtb/-without-signature.dtb/g"` - namewithsignature=`echo $ubootdevicetree | sed "s/\.dtb/-with-signature.dtb/g"` - mv $ubootdevicetree $namewithoutsignature - # get name of U-Boot device tree from DEPLOY_DIR - nameonkernel="${DEPLOY_DIR_IMAGE}/${FIP_UBOOT_DTB}-${devicetree}-with-signature.dtb" - cp $nameonkernel $namewithsignature - cp $nameonkernel $ubootdevicetree - done - fi } +do_uboot_assemble_fitimage:append:stm32mpcommon() { + for config in ${UBOOT_MACHINE}; do + if [ -n "${UBOOT_DEVICETREE}" ] && [ "${UBOOT_SIGN_ENABLE}" = "1" ]; then + for devicetree in ${UBOOT_DEVICETREE}; do + # Keep u-boot devicetree without signature + ubootdevicetree="${B}/${config}/arch/arm/dts/${devicetree}.dtb" + namewithoutsignature=`echo $ubootdevicetree | sed "s/\.dtb/-without-signature.dtb/g"` + cp $ubootdevicetree $namewithoutsignature + + # Add image public key in U-Boot dtb file + fdt_add_pubkey -a "${FIT_HASH_ALG},${FIT_SIGN_ALG}" \ + -k "${UBOOT_SIGN_KEYDIR}" \ + -n "${UBOOT_SIGN_IMG_KEYNAME}" \ + -r "image" \ + "${ubootdevicetree}" + + # Add configuration public key in U-Boot dtb file + fdt_add_pubkey -a "${FIT_HASH_ALG},${FIT_SIGN_ALG}" \ + -k "${UBOOT_SIGN_KEYDIR}" \ + -n "${UBOOT_SIGN_KEYNAME}" \ + -r "conf" \ + "${ubootdevicetree}" + done + fi + done +} +# Add dependency to make sure that RSA keys generated to sign fitImage are available for u-boot +do_uboot_assemble_fitimage[depends] += " \ + ${@'virtual/kernel:do_kernel_generate_rsa_keys' \ + if "stm32mpcommon" in d.getVar('MACHINEOVERRIDES') \ + and "fitImage" in d.getVar('KERNEL_IMAGETYPE') else ''} \ +" + FIP_DIR_UBOOT ?= "/u-boot" # Deploy u-boot artifacts, to be packaged in fip binary by fip-stm32mp recipe From d0dc8282f6968ae0f41317ce64b6653cc21a09cf Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Fri, 23 May 2025 11:57:30 +0200 Subject: [PATCH 110/138] u-boot-dey: safeguard rm operation in adapt_uboot_filenames to prevent build issues This commit fixes the rm command in the adapt_uboot_filenames function to prevent build failures when the target artifact does not exist, ensuring the operation is safe in all cases. https://onedigi.atlassian.net/browse/DEL-9634 Signed-off-by: Arturo Buzarra --- meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc index 7c9291d50..9c90fd148 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc @@ -145,7 +145,7 @@ adapt_uboot_filenames() { if [ $j -eq $i ] then cd ${DEPLOYDIR} - rm -r ${UBOOT_BINARY}-${type} + rm -f ${UBOOT_BINARY}-${type} ln -sf ${UBOOT_BINARYNAME}-${type}-${PV}-${PR}.${UBOOT_SUFFIX} ${UBOOT_BINARYNAME}-${type}.${UBOOT_SUFFIX} fi done From 24aef482efe5d0a0c08ec13101a676f84dc02556 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Wed, 4 Jun 2025 09:54:56 +0200 Subject: [PATCH 111/138] tf-a-stm32mp: remove STM32MP_USB_PROGRAMMER support from NAND images Adding STM32MP_USB_PROGRAMMER=1 to TF-A NAND build allows the images to boot from either NAND or USB (recovery) however, the source code of TF-A disallows correct resuming from suspend when either STM32MP_USB_PROGRAMMER or STM32MP_UART_PROGRAMMER are defined. Remove this support so that the system can correctly resume from suspend. Signed-off-by: Hector Palacios https://onedigi.atlassian.net/browse/DEL-9629 --- .../trusted-firmware-a/tf-a-stm32mp_2.10.bbappend | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend index 5e1423811..c25980125 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend @@ -24,18 +24,6 @@ SIGN_TOOL_EXTRA_soc:ccmp13 = " ${@bb.utils.contains('ENCRYPT_ENABLE', '1', '-of # stm32mp2 = header-version 2.2 SIGN_TOOL_EXTRA_soc:stm32mp2common = " --header-version 2.2 ${@bb.utils.contains('ENCRYPT_ENABLE', '1', '-of ${TF_A_ENCRYPT_OF}', '-of ${TF_A_SIGN_OF}', d)}" -TF_A_CONFIG[optee-nand] = "\ - ${STM32MP_DT_FILES_NAND},\ - ${TF_A_CONFIG_OPTS_optee} ${TF_A_CONFIG_OPTS_features} ${TF_A_CONFIG_OPTS_fwupdate} STM32MP_RAW_NAND=1 STM32MP_USB_PROGRAMMER=1 ${@'STM32MP_FORCE_MTD_START_OFFSET=${TF_A_MTD_START_OFFSET_NAND}' if ${TF_A_MTD_START_OFFSET_NAND} else ''},\ - ${TF_A_CONFIG_BASENAME_BIN},\ - ${TF_A_CONFIG_MAKE_TARGET},\ - ${TF_A_CONFIG_DEPLOY_FTYPE} ${TF_A_CONFIG_DEPLOY_EXTRA}" -TF_A_CONFIG[opteemin-nand] ?= "\ - ${STM32MP_DT_FILES_NAND},\ - ${TF_A_CONFIG_OPTS_optee} ${TF_A_CONFIG_OPTS_features} ${TF_A_CONFIG_OPTS_fwupdate} STM32MP_RAW_NAND=1 STM32MP_USB_PROGRAMMER=1 ${@'STM32MP_FORCE_MTD_START_OFFSET=${TF_A_MTD_START_OFFSET_NAND}' if ${TF_A_MTD_START_OFFSET_NAND} else ''},\ - ${TF_A_CONFIG_BASENAME_BIN},\ - ${TF_A_CONFIG_MAKE_TARGET},\ - ${TF_A_CONFIG_DEPLOY_FTYPE} ${TF_A_CONFIG_DEPLOY_EXTRA}" # TF_A_CONFIG[uart] (same as 'optee-programmer-uart') TF_A_CONFIG[uart] ?= "\ ${STM32MP_DEVICETREE_PROGRAMMER},\ From a2153e44ef9e3ad8213bd5d32fc112706e7d4a4b Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Wed, 4 Jun 2025 09:48:22 +0200 Subject: [PATCH 112/138] init-ifupdown: use dhcp for bridge connection When you create a bridge between Wi-Fi hostap and Ethernet, it's more convenient to let the bridge take an appropriate dynamic IP from the DHCP server, than needing to configure a manual one. Signed-off-by: Hector Palacios --- .../ccimx6sbc/interfaces.br0.atheros.example | 4 +--- .../init-ifupdown-1.0/ccimx6sbc/interfaces.br0.example | 4 +--- .../init-ifupdown-1.0/ccimx9/interfaces.br0.example | 4 +--- .../init-ifupdown/init-ifupdown-1.0/interfaces.br0.example | 4 +--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/meta-digi-dey/recipes-core/init-ifupdown/init-ifupdown-1.0/ccimx6sbc/interfaces.br0.atheros.example b/meta-digi-dey/recipes-core/init-ifupdown/init-ifupdown-1.0/ccimx6sbc/interfaces.br0.atheros.example index 4b78aaca9..c2b671a6e 100644 --- a/meta-digi-dey/recipes-core/init-ifupdown/init-ifupdown-1.0/ccimx6sbc/interfaces.br0.atheros.example +++ b/meta-digi-dey/recipes-core/init-ifupdown/init-ifupdown-1.0/ccimx6sbc/interfaces.br0.atheros.example @@ -1,10 +1,8 @@ ## Example bridge between eth0 and wlan0 (Atheros AR6233) #auto br0 -#iface br0 inet static +#iface br0 inet dhcp # bridge_ports eth0 wlan0 -# address 192.168.42.50 -# netmask 255.255.255.0 # bridge_wpa_driver nl80211 # bridge_wpa_conf /etc/wpa_supplicant.conf # pre-up [ $(find /sys/devices -name modalias -print0 | xargs -0 sort -u -z | grep sdio:c00v0271d0301) ] diff --git a/meta-digi-dey/recipes-core/init-ifupdown/init-ifupdown-1.0/ccimx6sbc/interfaces.br0.example b/meta-digi-dey/recipes-core/init-ifupdown/init-ifupdown-1.0/ccimx6sbc/interfaces.br0.example index 34545bee0..ac9023c04 100644 --- a/meta-digi-dey/recipes-core/init-ifupdown/init-ifupdown-1.0/ccimx6sbc/interfaces.br0.example +++ b/meta-digi-dey/recipes-core/init-ifupdown/init-ifupdown-1.0/ccimx6sbc/interfaces.br0.example @@ -1,8 +1,6 @@ ## Example bridge between eth0 and wlan1 (Qualcomm QCA65x4) #auto br0 -#iface br0 inet static +#iface br0 inet dhcp # bridge_ports eth0 wlan1 -# address 192.168.42.50 -# netmask 255.255.255.0 # pre-up [ $(find /sys/devices -name modalias -print0 | xargs -0 sort -u -z | grep sdio:c00v0271d050A) ] diff --git a/meta-digi-dey/recipes-core/init-ifupdown/init-ifupdown-1.0/ccimx9/interfaces.br0.example b/meta-digi-dey/recipes-core/init-ifupdown/init-ifupdown-1.0/ccimx9/interfaces.br0.example index 6cfe96a72..9f5ad5771 100644 --- a/meta-digi-dey/recipes-core/init-ifupdown/init-ifupdown-1.0/ccimx9/interfaces.br0.example +++ b/meta-digi-dey/recipes-core/init-ifupdown/init-ifupdown-1.0/ccimx9/interfaces.br0.example @@ -1,8 +1,6 @@ ## Example bridge between eth0 and uap0 (NXP IW612) #auto br0 -#iface br0 inet static +#iface br0 inet dhcp # bridge_ports eth0 uap0 -# address 192.168.42.50 -# netmask 255.255.255.0 # pre-up [ -d /proc/device-tree/wireless ] diff --git a/meta-digi-dey/recipes-core/init-ifupdown/init-ifupdown-1.0/interfaces.br0.example b/meta-digi-dey/recipes-core/init-ifupdown/init-ifupdown-1.0/interfaces.br0.example index b04f821dc..bbba3f077 100644 --- a/meta-digi-dey/recipes-core/init-ifupdown/init-ifupdown-1.0/interfaces.br0.example +++ b/meta-digi-dey/recipes-core/init-ifupdown/init-ifupdown-1.0/interfaces.br0.example @@ -1,8 +1,6 @@ ## Example bridge between eth0 and wlan1 #auto br0 -#iface br0 inet static +#iface br0 inet dhcp # bridge_ports eth0 wlan1 -# address 192.168.42.50 -# netmask 255.255.255.0 # pre-up [ -d /proc/device-tree/wireless ] From 0fa18775ad52cedc4ac740bd70aca7913a2bac66 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Fri, 6 Jun 2025 12:30:33 +0200 Subject: [PATCH 113/138] dey-toolchain: fix including machine-specific SDK tools Fixes commit b143804dbb1f219b4c3de5253a634cc7bdf2bbc0, since in nativesdk context MACHINE_FEATURES is reset to SDK_MACHINE_FEATURES, causing OP-TEE building tools to be missing from the generated SDK. https://onedigi.atlassian.net/browse/DEL-9663 Signed-off-by: Javier Viguera --- meta-digi-arm/conf/machine/include/ccimx8m.inc | 4 ++++ meta-digi-arm/conf/machine/include/ccimx9.inc | 4 ++++ .../packagegroup/nativesdk-packagegroup-sdk-host.bbappend | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/meta-digi-arm/conf/machine/include/ccimx8m.inc b/meta-digi-arm/conf/machine/include/ccimx8m.inc index bb2d79f7c..c164d867a 100644 --- a/meta-digi-arm/conf/machine/include/ccimx8m.inc +++ b/meta-digi-arm/conf/machine/include/ccimx8m.inc @@ -54,6 +54,10 @@ MACHINE_EXTRA_RRECOMMENDS += "${WIRELESS_MODULE}" MACHINE_FEATURES += "accel-graphics wifi bluetooth cryptochip mca optee" +# For nativesdk packages that need to go to the toolchain depending on an +# specific machine feature. +SDK_MACHINE_FEATURES:append = " optee" + # TrustFence TRUSTFENCE_SIGN_MODE = "HAB" diff --git a/meta-digi-arm/conf/machine/include/ccimx9.inc b/meta-digi-arm/conf/machine/include/ccimx9.inc index 1eb93c2a1..c6bc929d5 100644 --- a/meta-digi-arm/conf/machine/include/ccimx9.inc +++ b/meta-digi-arm/conf/machine/include/ccimx9.inc @@ -40,6 +40,10 @@ DDR_FIRMWARE_NAME = " \ MACHINE_FEATURES += "pci wifi bluetooth" MACHINE_FEATURES:append:use-nxp-bsp = " optee jailhouse" +# For nativesdk packages that need to go to the toolchain depending on an +# specific machine feature. +SDK_MACHINE_FEATURES:append = " optee" + STORAGE_MEDIA = "mmc" WLAN_P2P_INTERFACE = "wfd0" diff --git a/meta-digi-arm/recipes-core/packagegroup/nativesdk-packagegroup-sdk-host.bbappend b/meta-digi-arm/recipes-core/packagegroup/nativesdk-packagegroup-sdk-host.bbappend index 1df3246f1..e9697af51 100644 --- a/meta-digi-arm/recipes-core/packagegroup/nativesdk-packagegroup-sdk-host.bbappend +++ b/meta-digi-arm/recipes-core/packagegroup/nativesdk-packagegroup-sdk-host.bbappend @@ -1,6 +1,6 @@ # Copyright (C) 2016-2025, Digi International Inc. -IMX_OPTEE_SDK_RDEPENDS ?= "${@bb.utils.contains('MACHINE_FEATURES', 'optee', \ +IMX_OPTEE_SDK_RDEPENDS ?= "${@bb.utils.contains('SDK_MACHINE_FEATURES', 'optee', \ 'nativesdk-python3-cryptography nativesdk-python3-pyelftools', \ '', d)}" From 5271924af70f8be6a90e7b15d460c58627ed7a83 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Tue, 10 Jun 2025 16:09:25 +0200 Subject: [PATCH 114/138] Revert "tf-a-stm32mp: remove STM32MP_USB_PROGRAMMER support from NAND images" We reverted the stub that didn't allow PM when serial boot was enabled on TF-A. Restore the part of the recipe that includes USB boot support on NAND boot images. This reverts commit 24aef482efe5d0a0c08ec13101a676f84dc02556. Signed-off-by: Hector Palacios https://onedigi.atlassian.net/browse/DEL-9629 --- .../trusted-firmware-a/tf-a-stm32mp_2.10.bbappend | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend index c25980125..5e1423811 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend @@ -24,6 +24,18 @@ SIGN_TOOL_EXTRA_soc:ccmp13 = " ${@bb.utils.contains('ENCRYPT_ENABLE', '1', '-of # stm32mp2 = header-version 2.2 SIGN_TOOL_EXTRA_soc:stm32mp2common = " --header-version 2.2 ${@bb.utils.contains('ENCRYPT_ENABLE', '1', '-of ${TF_A_ENCRYPT_OF}', '-of ${TF_A_SIGN_OF}', d)}" +TF_A_CONFIG[optee-nand] = "\ + ${STM32MP_DT_FILES_NAND},\ + ${TF_A_CONFIG_OPTS_optee} ${TF_A_CONFIG_OPTS_features} ${TF_A_CONFIG_OPTS_fwupdate} STM32MP_RAW_NAND=1 STM32MP_USB_PROGRAMMER=1 ${@'STM32MP_FORCE_MTD_START_OFFSET=${TF_A_MTD_START_OFFSET_NAND}' if ${TF_A_MTD_START_OFFSET_NAND} else ''},\ + ${TF_A_CONFIG_BASENAME_BIN},\ + ${TF_A_CONFIG_MAKE_TARGET},\ + ${TF_A_CONFIG_DEPLOY_FTYPE} ${TF_A_CONFIG_DEPLOY_EXTRA}" +TF_A_CONFIG[opteemin-nand] ?= "\ + ${STM32MP_DT_FILES_NAND},\ + ${TF_A_CONFIG_OPTS_optee} ${TF_A_CONFIG_OPTS_features} ${TF_A_CONFIG_OPTS_fwupdate} STM32MP_RAW_NAND=1 STM32MP_USB_PROGRAMMER=1 ${@'STM32MP_FORCE_MTD_START_OFFSET=${TF_A_MTD_START_OFFSET_NAND}' if ${TF_A_MTD_START_OFFSET_NAND} else ''},\ + ${TF_A_CONFIG_BASENAME_BIN},\ + ${TF_A_CONFIG_MAKE_TARGET},\ + ${TF_A_CONFIG_DEPLOY_FTYPE} ${TF_A_CONFIG_DEPLOY_EXTRA}" # TF_A_CONFIG[uart] (same as 'optee-programmer-uart') TF_A_CONFIG[uart] ?= "\ ${STM32MP_DEVICETREE_PROGRAMMER},\ From 5156fe26b318247dd3d9a1a115c717cb757ae1ce Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Thu, 5 Jun 2025 11:33:44 +0200 Subject: [PATCH 115/138] installers: use if/elif/else to determine rootfs filename The installer uses the regular rootfs filename or falls back to search for a squashfs (read-only) rootfs image. The UUU installers of eMMC-based platforms use an if/elif/else structure to determine which file exist (in order of preference). Replicate this structure on the rest of platforms and installers. This avoids printing an error message if the default rootfs does not exist but the read-only one does. Also, reset 'rootfstype' variable if the default rootfs file exists, which allows to install regular images over a previous read-only system. Signed-off-by: Hector Palacios --- .../ccimx6qpsbc/install_linux_fw_sd.txt | 18 +++++++-------- .../ccimx6qpsbc/install_linux_fw_usb.txt | 18 +++++++-------- .../ccimx6sbc/install_linux_fw_sd.txt | 18 +++++++-------- .../ccimx6sbc/install_linux_fw_usb.txt | 18 +++++++-------- .../ccimx6ul/install_linux_fw_sd.txt | 22 +++++++++---------- .../ccimx6ul/install_linux_fw_usb.txt | 22 +++++++++---------- .../ccimx6ul/install_linux_fw_uuu.sh | 21 +++++++++--------- .../ccimx8m/install_linux_fw_sd.txt | 18 +++++++-------- .../ccimx8m/install_linux_fw_usb.txt | 18 +++++++-------- .../ccimx8x/install_linux_fw_sd.txt | 18 +++++++-------- .../ccimx8x/install_linux_fw_usb.txt | 18 +++++++-------- .../u-boot-dey/ccimx9/install_linux_fw_sd.txt | 18 +++++++-------- .../ccimx9/install_linux_fw_usb.txt | 18 +++++++-------- .../u-boot-dey/ccmp1/install_linux_fw_sd.txt | 22 +++++++++---------- .../u-boot-dey/ccmp1/install_linux_fw_usb.txt | 22 +++++++++---------- .../u-boot-dey/ccmp1/install_linux_fw_uuu.sh | 21 +++++++++--------- .../u-boot-dey/ccmp2/install_linux_fw_sd.txt | 18 +++++++-------- .../u-boot-dey/ccmp2/install_linux_fw_usb.txt | 18 +++++++-------- 18 files changed, 170 insertions(+), 176 deletions(-) diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_sd.txt index b83524d0a..a44d894d4 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_sd.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_sd.txt @@ -53,7 +53,6 @@ if test "${BASEFILENAME}" = "0"; then fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ext4 # Check for presence of firmware files on the SD card FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -67,14 +66,15 @@ for install_f in ${FILES}; do fi; done # Verify what kind of rootfs is going to be programmed -if test ! -e mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.squashfs - echo "INFO: Trying with file ${INSTALL_ROOTFS_FILENAME}"; - if test ! -e mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - install_abort=1; - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; +elif test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME_SQFS}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; +else + echo "ERROR: Could not find any rootfs image"; + install_abort=1; fi if test "${install_abort}" = "1"; then echo "To install a different image, set variable 'image-name', e.g.:" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_usb.txt index 1881db30e..57aa04d39 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_usb.txt @@ -53,7 +53,6 @@ if test "${BASEFILENAME}" = "0"; then fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ext4 # Check for presence of firmware files on the USB FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -67,14 +66,15 @@ for install_f in ${FILES}; do fi; done # Verify what kind of rootfs is going to be programmed -if test ! -e usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.squashfs - echo "INFO: Trying with file ${INSTALL_ROOTFS_FILENAME}"; - if test ! -e usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - install_abort=1; - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; +elif test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME_SQFS}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; +else + echo "ERROR: Could not find any rootfs image"; + install_abort=1; fi if test "${install_abort}" = "1"; then echo "To install a different image, set variable 'image-name', e.g.:" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_sd.txt index aa426b077..9d5e7a68e 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_sd.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_sd.txt @@ -60,7 +60,6 @@ if test "${BASEFILENAME}" = "0"; then fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ext4 # Check for presence of firmware files on the SD card FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -74,14 +73,15 @@ for install_f in ${FILES}; do fi; done # Verify what kind of rootfs is going to be programmed -if test ! -e mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.squashfs - echo "INFO: Trying with file ${INSTALL_ROOTFS_FILENAME}"; - if test ! -e mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - install_abort=1; - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; +elif test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME_SQFS}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; +else + echo "ERROR: Could not find any rootfs image"; + install_abort=1; fi if test "${install_abort}" = "1"; then echo "To install a different image, set variable 'image-name', e.g.:" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_usb.txt index ddc141d94..984df8e76 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_usb.txt @@ -60,7 +60,6 @@ if test "${BASEFILENAME}" = "0"; then fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ext4 # Check for presence of firmware files on the USB FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -74,14 +73,15 @@ for install_f in ${FILES}; do fi; done # Verify what kind of rootfs is going to be programmed -if test ! -e usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.squashfs - echo "INFO: Trying with file ${INSTALL_ROOTFS_FILENAME}"; - if test ! -e usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - install_abort=1; - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; +elif test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME_SQFS}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; +else + echo "ERROR: Could not find any rootfs image"; + install_abort=1; fi if test "${install_abort}" = "1"; then echo "To install a different image, set variable 'image-name', e.g.:" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_sd.txt index 61b3bb892..d4cb888a4 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_sd.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_sd.txt @@ -56,7 +56,6 @@ if test "${BASEFILENAME}" = "0"; then fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.ubifs setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.ubifs -setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ubifs # Check for presence of firmware files on the SD card FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -70,16 +69,17 @@ for install_f in ${FILES}; do fi; done # Verify what kind of rootfs is going to be programmed -if test ! -e mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.squashfs - echo "INFO: Trying with file ${INSTALL_ROOTFS_FILENAME}"; - if test ! -e mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - install_abort=1; - else - setenv rootfstype squashfs - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ubifs" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; + setenv rootfstype; +elif test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME_SQFS}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; + setenv rootfstype squashfs; +else + echo "ERROR: Could not find any rootfs image"; + install_abort=1; fi if test "${install_abort}" = "1"; then echo "To install a different image, set variable 'image-name', e.g.:" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_usb.txt index 5d8b6e7ce..7b20779ea 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_usb.txt @@ -56,7 +56,6 @@ if test "${BASEFILENAME}" = "0"; then fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.ubifs setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.ubifs -setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ubifs # Check for presence of firmware files on the USB FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -70,18 +69,17 @@ for install_f in ${FILES}; do fi; done # Verify what kind of rootfs is going to be programmed -if test ! -e usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.squashfs - echo "INFO: Trying with file ${INSTALL_ROOTFS_FILENAME}"; - if test ! -e usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - install_abort=1; - else - setenv rootfstype squashfs - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ubifs" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; + setenv rootfstype; +elif test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME_SQFS}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; + setenv rootfstype squashfs; else - setenv rootfstype + echo "ERROR: Could not find any rootfs image"; + install_abort=1; fi if test "${install_abort}" = "1"; then echo "To install a different image, set variable 'image-name', e.g.:" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_uuu.sh b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_uuu.sh index aed735e7b..b3c3f862d 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_uuu.sh +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_uuu.sh @@ -202,7 +202,6 @@ if [ -z "${BASEFILENAME}" ]; then fi INSTALL_LINUX_FILENAME="${BASEFILENAME}-##MACHINE##.boot.ubifs" INSTALL_RECOVERY_FILENAME="${BASEFILENAME}-##MACHINE##.recovery.ubifs" -INSTALL_ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ubifs" # Verify existence of files before starting the update FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -217,16 +216,16 @@ for f in ${FILES}; do done; # Verify what kind of rootfs is going to be programmed -if [ ! -f ${INSTALL_ROOTFS_FILENAME} ]; then - echo "\033[31m[ERROR] Could not find file '${INSTALL_ROOTFS_FILENAME}'\033[0m" - INSTALL_ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.squashfs" - echo "\033[32m[INFO] Trying with file '${INSTALL_ROOTFS_FILENAME}'\033[0m" - if [ -f "${INSTALL_ROOTFS_FILENAME}" ]; then - SQUASHFS=true - else - echo "\033[31m[ERROR] Could not find file '${INSTALL_ROOTFS_FILENAME}'\033[0m" - ABORT=true - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ubifs" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if [ -f "${ROOTFS_FILENAME}" ]; then + INSTALL_ROOTFS_FILENAME="${ROOTFS_FILENAME}" +elif [ -f "${ROOTFS_FILENAME_SQFS}" ]; then + INSTALL_ROOTFS_FILENAME="${ROOTFS_FILENAME_SQFS}" + SQUASHFS=true +else + printf "\033[31m[ERROR] Could not find any rootfs image\033[0m\n" + ABORT=true fi [ "${ABORT}" = true ] && exit 1 diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_sd.txt index 466f98c81..4d2b76809 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_sd.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_sd.txt @@ -24,7 +24,6 @@ if test "${BASEFILENAME}" = "0"; then fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ext4 # Check for presence of firmware files on the SD card FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -38,14 +37,15 @@ for install_f in ${FILES}; do fi; done # Verify what kind of rootfs is going to be programmed -if test ! -e mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.squashfs - echo "INFO: Trying with file ${INSTALL_ROOTFS_FILENAME}"; - if test ! -e mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - install_abort=1; - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; +elif test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME_SQFS}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; +else + echo "ERROR: Could not find any rootfs image"; + install_abort=1; fi if test "${install_abort}" = "1"; then echo "To install a different image, set variable 'image-name', e.g.:" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_usb.txt index 4635ae21c..338fc7fd8 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_usb.txt @@ -24,7 +24,6 @@ if test "${BASEFILENAME}" = "0"; then fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ext4 # Check for presence of firmware files on the USB FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -38,14 +37,15 @@ for install_f in ${FILES}; do fi; done # Verify what kind of rootfs is going to be programmed -if test ! -e usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.squashfs - echo "INFO: Trying with file ${INSTALL_ROOTFS_FILENAME}"; - if test ! -e usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - install_abort=1; - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; +elif test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME_SQFS}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; +else + echo "ERROR: Could not find any rootfs image"; + install_abort=1; fi if test "${install_abort}" = "1"; then echo "To install a different image, set variable 'image-name', e.g.:" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_sd.txt index 4ff66b973..1c777d062 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_sd.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_sd.txt @@ -36,7 +36,6 @@ if test "${BASEFILENAME}" = "0"; then fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ext4 # Check for presence of firmware files on the SD card FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -50,14 +49,15 @@ for install_f in ${FILES}; do fi; done # Verify what kind of rootfs is going to be programmed -if test ! -e mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.squashfs - echo "INFO: Trying with file ${INSTALL_ROOTFS_FILENAME}"; - if test ! -e mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - install_abort=1; - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; +elif test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME_SQFS}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; +else + echo "ERROR: Could not find any rootfs image"; + install_abort=1; fi if test "${install_abort}" = "1"; then echo "To install a different image, set variable 'image-name', e.g.:" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_usb.txt index f75804f65..9dd742845 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_usb.txt @@ -36,7 +36,6 @@ if test "${BASEFILENAME}" = "0"; then fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ext4 # Check for presence of firmware files on the USB FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -50,14 +49,15 @@ for install_f in ${FILES}; do fi; done # Verify what kind of rootfs is going to be programmed -if test ! -e usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.squashfs - echo "INFO: Trying with file ${INSTALL_ROOTFS_FILENAME}"; - if test ! -e usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - install_abort=1; - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; +elif test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME_SQFS}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; +else + echo "ERROR: Could not find any rootfs image"; + install_abort=1; fi if test "${install_abort}" = "1"; then echo "To install a different image, set variable 'image-name', e.g.:" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_sd.txt index 1ca68a2cd..a5ba55987 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_sd.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_sd.txt @@ -39,7 +39,6 @@ if test "${BASEFILENAME}" = "0"; then fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ext4 # Check for presence of firmware files on the SD card FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -53,14 +52,15 @@ for install_f in ${FILES}; do fi; done # Verify what kind of rootfs is going to be programmed -if test ! -e mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.squashfs - echo "INFO: Trying with file ${INSTALL_ROOTFS_FILENAME}"; - if test ! -e mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - install_abort=1; - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; +elif test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME_SQFS}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; +else + echo "ERROR: Could not find any rootfs image"; + install_abort=1; fi if test "${install_abort}" = "1"; then echo "To install a different image, set variable 'image-name', e.g.:" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_usb.txt index 9fc05dd8c..5d1ac44d6 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_usb.txt @@ -39,7 +39,6 @@ if test "${BASEFILENAME}" = "0"; then fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ext4 # Check for presence of firmware files on the USB FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -53,14 +52,15 @@ for install_f in ${FILES}; do fi; done # Verify what kind of rootfs is going to be programmed -if test ! -e usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.squashfs - echo "INFO: Trying with file ${INSTALL_ROOTFS_FILENAME}"; - if test ! -e usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - install_abort=1; - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; +elif test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME_SQFS}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; +else + echo "ERROR: Could not find any rootfs image"; + install_abort=1; fi if test "${install_abort}" = "1"; then echo "To install a different image, set variable 'image-name', e.g.:" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_sd.txt index feb94734e..d5c42709c 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_sd.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_sd.txt @@ -26,7 +26,6 @@ if test "${BASEFILENAME}" = "0"; then fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.ubifs setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.ubifs -setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ubifs # Check for presence of firmware files on the SD card FILES="${INSTALL_ATF_FILENAME} ${INSTALL_METADATA_FILENAME} ${INSTALL_FIP_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -40,16 +39,17 @@ for install_f in ${FILES}; do fi; done # Verify what kind of rootfs is going to be programmed -if test ! -e mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.squashfs - echo "INFO: Trying with file ${INSTALL_ROOTFS_FILENAME}"; - if test ! -e mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - install_abort=1; - else - setenv rootfstype squashfs - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ubifs" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; + setenv rootfstype; +elif test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME_SQFS}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; + setenv rootfstype squashfs; +else + echo "ERROR: Could not find any rootfs image"; + install_abort=1; fi if test "${install_abort}" = "1"; then echo "To install a different image, set variable 'image-name', e.g.:" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_usb.txt index c491b64ee..3173db8f9 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_usb.txt @@ -26,7 +26,6 @@ if test "${BASEFILENAME}" = "0"; then fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.ubifs setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.ubifs -setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ubifs # Check for presence of firmware files on the USB FILES="${INSTALL_ATF_FILENAME} ${INSTALL_METADATA_FILENAME} ${INSTALL_FIP_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -40,18 +39,17 @@ for install_f in ${FILES}; do fi; done # Verify what kind of rootfs is going to be programmed -if test ! -e usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.squashfs - echo "INFO: Trying with file ${INSTALL_ROOTFS_FILENAME}"; - if test ! -e usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - install_abort=1; - else - setenv rootfstype squashfs - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ubifs" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; + setenv rootfstype; +elif test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME_SQFS}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; + setenv rootfstype squashfs; else - setenv rootfstype + echo "ERROR: Could not find any rootfs image"; + install_abort=1; fi if test "${install_abort}" = "1"; then echo "To install a different image, set variable 'image-name', e.g.:" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_uuu.sh b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_uuu.sh index 031202e1e..4df790ea4 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_uuu.sh +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_uuu.sh @@ -152,7 +152,6 @@ if [ -z "${BASEFILENAME}" ]; then fi INSTALL_LINUX_FILENAME="${BASEFILENAME}-##MACHINE##.boot.ubifs" INSTALL_RECOVERY_FILENAME="${BASEFILENAME}-##MACHINE##.recovery.ubifs" -INSTALL_ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ubifs" # Verify existence of files before starting the update FILES="${INSTALL_ATF_FILENAME} ${INSTALL_METADATA_FILENAME} ${INSTALL_FIP_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -167,16 +166,16 @@ for f in ${FILES}; do done; # Verify what kind of rootfs is going to be programmed -if [ ! -f ${INSTALL_ROOTFS_FILENAME} ]; then - printf "\033[31m[ERROR] Could not find file '${INSTALL_ROOTFS_FILENAME}'\033[0m\n" - INSTALL_ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.squashfs" - printf "\033[32m[INFO] Trying with file '${INSTALL_ROOTFS_FILENAME}'\033[0m\n" - if [ -f "${INSTALL_ROOTFS_FILENAME}" ]; then - SQUASHFS=true - else - printf "\033[31m[ERROR] Could not find any rootfs image\033[0m\n" - ABORT=true - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ubifs" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if [ -f "${ROOTFS_FILENAME}" ]; then + INSTALL_ROOTFS_FILENAME="${ROOTFS_FILENAME}" +elif [ -f "${ROOTFS_FILENAME_SQFS}" ]; then + INSTALL_ROOTFS_FILENAME="${ROOTFS_FILENAME_SQFS}" + SQUASHFS=true +else + printf "\033[31m[ERROR] Could not find any rootfs image\033[0m\n" + ABORT=true fi [ "${ABORT}" = true ] && exit 1 diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_sd.txt index 46b3df7fe..bc3c38c8a 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_sd.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_sd.txt @@ -26,7 +26,6 @@ if test "${BASEFILENAME}" = "0"; then fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ext4 # Check for presence of firmware files on the SD card FILES="${INSTALL_ATF_FILENAME} ${INSTALL_METADATA_FILENAME} ${INSTALL_FIP_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -40,14 +39,15 @@ for install_f in ${FILES}; do fi; done # Verify what kind of rootfs is going to be programmed -if test ! -e mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.squashfs - echo "INFO: Trying with file ${INSTALL_ROOTFS_FILENAME}"; - if test ! -e mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - install_abort=1; - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; +elif test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME_SQFS}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; +else + echo "ERROR: Could not find any rootfs image"; + install_abort=1; fi if test "${install_abort}" = "1"; then echo "To install a different image, set variable 'image-name', e.g.:" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_usb.txt index ae5bd9ae7..32235698c 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_usb.txt @@ -26,7 +26,6 @@ if test "${BASEFILENAME}" = "0"; then fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.ext4 # Check for presence of firmware files on the USB FILES="${INSTALL_ATF_FILENAME} ${INSTALL_METADATA_FILENAME} ${INSTALL_FIP_FILENAME} ${INSTALL_LINUX_FILENAME}" @@ -40,14 +39,15 @@ for install_f in ${FILES}; do fi; done # Verify what kind of rootfs is going to be programmed -if test ! -e usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - setenv INSTALL_ROOTFS_FILENAME ${BASEFILENAME}-##MACHINE##.squashfs - echo "INFO: Trying with file ${INSTALL_ROOTFS_FILENAME}"; - if test ! -e usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; then - echo "ERROR: Could not find file ${INSTALL_ROOTFS_FILENAME}"; - install_abort=1; - fi +ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" +ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" +if test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; +elif test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME_SQFS}; then + setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; +else + echo "ERROR: Could not find any rootfs image"; + install_abort=1; fi if test "${install_abort}" = "1"; then echo "To install a different image, set variable 'image-name', e.g.:" From ac69566ecdbda1d5183dd5618fb9fb0356a479b6 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Thu, 5 Jun 2025 16:10:53 +0200 Subject: [PATCH 116/138] installer: merge USB/SD installers into a single template The USB and SD installers are U-Boot scripts that are practically identical. Merge them into a single template with a couple of machine variables that determine the default device index in U-Boot for the USB or the microSD card. Do dynamic substitutions to create the two installers out of the template. Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/ccimx6qpsbc.conf | 4 + meta-digi-arm/conf/machine/ccimx6sbc.conf | 4 + meta-digi-arm/conf/machine/ccimx6ulsbc.conf | 4 + .../conf/machine/ccimx6ulstarter.conf | 4 + meta-digi-arm/conf/machine/ccimx8mm-dvk.conf | 5 + meta-digi-arm/conf/machine/ccimx8mn-dvk.conf | 5 + .../conf/machine/ccimx8x-sbc-express.conf | 4 + .../conf/machine/ccimx8x-sbc-pro.conf | 4 + meta-digi-arm/conf/machine/ccimx91-dvk.conf | 5 + meta-digi-arm/conf/machine/ccimx93-dvk.conf | 5 + meta-digi-arm/conf/machine/ccmp13-dvk.conf | 4 + meta-digi-arm/conf/machine/ccmp15-dvk.conf | 4 + meta-digi-arm/conf/machine/ccmp25-dvk.conf | 4 + .../recipes-bsp/u-boot/u-boot-dey.inc | 12 +- ..._fw_usb.txt => install_linux_fw_media.txt} | 39 ++- .../ccimx6qpsbc/install_linux_fw_sd.txt | 285 ---------------- ..._fw_usb.txt => install_linux_fw_media.txt} | 39 ++- .../ccimx6sbc/install_linux_fw_sd.txt | 292 ---------------- ..._fw_usb.txt => install_linux_fw_media.txt} | 40 ++- .../ccimx6ul/install_linux_fw_sd.txt | 290 ---------------- ..._fw_usb.txt => install_linux_fw_media.txt} | 42 ++- .../ccimx8m/install_linux_fw_sd.txt | 263 --------------- ..._fw_usb.txt => install_linux_fw_media.txt} | 43 +-- .../ccimx8x/install_linux_fw_sd.txt | 275 ---------------- ..._fw_usb.txt => install_linux_fw_media.txt} | 43 ++- .../u-boot-dey/ccimx9/install_linux_fw_sd.txt | 296 ----------------- ..._fw_usb.txt => install_linux_fw_media.txt} | 47 +-- .../u-boot-dey/ccmp1/install_linux_fw_sd.txt | 303 ----------------- ..._fw_usb.txt => install_linux_fw_media.txt} | 49 +-- .../u-boot-dey/ccmp2/install_linux_fw_sd.txt | 311 ------------------ 30 files changed, 261 insertions(+), 2464 deletions(-) rename meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/{install_linux_fw_usb.txt => install_linux_fw_media.txt} (85%) delete mode 100644 meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_sd.txt rename meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/{install_linux_fw_usb.txt => install_linux_fw_media.txt} (86%) delete mode 100644 meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_sd.txt rename meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/{install_linux_fw_usb.txt => install_linux_fw_media.txt} (85%) delete mode 100644 meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_sd.txt rename meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/{install_linux_fw_usb.txt => install_linux_fw_media.txt} (81%) delete mode 100644 meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_sd.txt rename meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/{install_linux_fw_usb.txt => install_linux_fw_media.txt} (83%) delete mode 100644 meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_sd.txt rename meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/{install_linux_fw_usb.txt => install_linux_fw_media.txt} (83%) delete mode 100644 meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_sd.txt rename meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/{install_linux_fw_usb.txt => install_linux_fw_media.txt} (82%) delete mode 100644 meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_sd.txt rename meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/{install_linux_fw_usb.txt => install_linux_fw_media.txt} (81%) delete mode 100644 meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_sd.txt diff --git a/meta-digi-arm/conf/machine/ccimx6qpsbc.conf b/meta-digi-arm/conf/machine/ccimx6qpsbc.conf index 5651ae2b8..5f9488515 100644 --- a/meta-digi-arm/conf/machine/ccimx6qpsbc.conf +++ b/meta-digi-arm/conf/machine/ccimx6qpsbc.conf @@ -25,6 +25,10 @@ MACHINE_FIRMWARE:append = " ${@oe.utils.conditional('HAVE_WIFI', '1', 'firmware- UBOOT_CONFIG ??= "ccimx6qpsbc1GB ccimx6qpsbc2GB" UBOOT_CONFIG[ccimx6qpsbc1GB] = "ccimx6qpsbc1GB_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" UBOOT_CONFIG[ccimx6qpsbc2GB] = "ccimx6qpsbc2GB_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" +# Index of USB device for installer +UBOOT_INSTALL_USB_INDEX = "0" +# Index of microSD device for installer +UBOOT_INSTALL_SD_INDEX = "1" KERNEL_DEVICETREE ?= " \ imx6qp-ccimx6qpsbc.dtb \ diff --git a/meta-digi-arm/conf/machine/ccimx6sbc.conf b/meta-digi-arm/conf/machine/ccimx6sbc.conf index 7ff307e24..0f38026c2 100644 --- a/meta-digi-arm/conf/machine/ccimx6sbc.conf +++ b/meta-digi-arm/conf/machine/ccimx6sbc.conf @@ -23,6 +23,10 @@ UBOOT_CONFIG[ccimx6dlsbc] = "ccimx6dlsbc_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" UBOOT_CONFIG[ccimx6qsbc2GB] = "ccimx6qsbc2GB_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" UBOOT_CONFIG[ccimx6qsbc512MB] = "ccimx6qsbc512MB_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" UBOOT_CONFIG[ccimx6qsbc] = "ccimx6qsbc_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" +# Index of USB device for installer +UBOOT_INSTALL_USB_INDEX = "0" +# Index of microSD device for installer +UBOOT_INSTALL_SD_INDEX = "1" KERNEL_DEVICETREE ?= " \ imx6dl-ccimx6sbc.dtb \ diff --git a/meta-digi-arm/conf/machine/ccimx6ulsbc.conf b/meta-digi-arm/conf/machine/ccimx6ulsbc.conf index be760aa50..f9667d9fb 100644 --- a/meta-digi-arm/conf/machine/ccimx6ulsbc.conf +++ b/meta-digi-arm/conf/machine/ccimx6ulsbc.conf @@ -11,6 +11,10 @@ UBOOT_CONFIG ??= "ccimx6ulsbc1GB ccimx6ulsbc512MB ccimx6ulsbc" UBOOT_CONFIG[ccimx6ulsbc1GB] = "ccimx6ulsbc1GB_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" UBOOT_CONFIG[ccimx6ulsbc512MB] = "ccimx6ulsbc512MB_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" UBOOT_CONFIG[ccimx6ulsbc] = "ccimx6ulsbc_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" +# Index of USB device for installer +UBOOT_INSTALL_USB_INDEX = "0" +# Index of microSD device for installer +UBOOT_INSTALL_SD_INDEX = "0" KERNEL_DEVICETREE ?= " \ digi/imx6ul-ccimx6ulsbc.dtb \ diff --git a/meta-digi-arm/conf/machine/ccimx6ulstarter.conf b/meta-digi-arm/conf/machine/ccimx6ulstarter.conf index 3dbdb5210..2d653a4a6 100644 --- a/meta-digi-arm/conf/machine/ccimx6ulstarter.conf +++ b/meta-digi-arm/conf/machine/ccimx6ulstarter.conf @@ -11,6 +11,10 @@ UBOOT_CONFIG ??= "ccimx6ulstarter1GB ccimx6ulstarter512MB ccimx6ulstarter" UBOOT_CONFIG[ccimx6ulstarter1GB] = "ccimx6ulstarter1GB_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" UBOOT_CONFIG[ccimx6ulstarter512MB] = "ccimx6ulstarter512MB_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" UBOOT_CONFIG[ccimx6ulstarter] = "ccimx6ulstarter_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" +# Index of USB device for installer +UBOOT_INSTALL_USB_INDEX = "0" +# Index of microSD device for installer +UBOOT_INSTALL_SD_INDEX = "0" KERNEL_DEVICETREE ?= " \ digi/imx6ul-ccimx6ulstarter.dtb \ diff --git a/meta-digi-arm/conf/machine/ccimx8mm-dvk.conf b/meta-digi-arm/conf/machine/ccimx8mm-dvk.conf index ca47077fb..5795adf16 100644 --- a/meta-digi-arm/conf/machine/ccimx8mm-dvk.conf +++ b/meta-digi-arm/conf/machine/ccimx8mm-dvk.conf @@ -22,6 +22,11 @@ SPL_BINARY = "spl/u-boot-spl.bin" # Set u-boot DTB UBOOT_DTB_NAME = "ccimx8mm-dvk.dtb" +# Index of USB device for installer +UBOOT_INSTALL_USB_INDEX = "0" +# Index of microSD device for installer +UBOOT_INSTALL_SD_INDEX = "1" + ATF_PLATFORM = "imx8mm" IMX_BOOT_SOC_TARGET = "iMX8MM" diff --git a/meta-digi-arm/conf/machine/ccimx8mn-dvk.conf b/meta-digi-arm/conf/machine/ccimx8mn-dvk.conf index ffcd958bc..b546c00bc 100644 --- a/meta-digi-arm/conf/machine/ccimx8mn-dvk.conf +++ b/meta-digi-arm/conf/machine/ccimx8mn-dvk.conf @@ -21,6 +21,11 @@ SPL_BINARY = "spl/u-boot-spl.bin" # Set u-boot DTB UBOOT_DTB_NAME = "ccimx8mn-dvk.dtb" +# Index of USB device for installer +UBOOT_INSTALL_USB_INDEX = "0" +# Index of microSD device for installer +UBOOT_INSTALL_SD_INDEX = "1" + ATF_PLATFORM = "imx8mn" IMX_BOOT_SOC_TARGET = "iMX8MN" diff --git a/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf b/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf index 58a823e02..1644cb6c7 100644 --- a/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf +++ b/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf @@ -9,6 +9,10 @@ include conf/machine/include/ccimx8x.inc UBOOT_CONFIG ??= "ccimx8x_sbc_express" UBOOT_CONFIG[ccimx8x_sbc_express] = "ccimx8x_sbc_express_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" SPL_BINARY = "spl/u-boot-spl.bin" +# Index of USB device for installer +UBOOT_INSTALL_USB_INDEX = "0" +# Index of microSD device for installer +UBOOT_INSTALL_SD_INDEX = "1" KERNEL_DEVICETREE ?= " \ digi/ccimx8x_bt.dtbo \ diff --git a/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf b/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf index 778992419..073faefe4 100644 --- a/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf +++ b/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf @@ -10,6 +10,10 @@ include conf/machine/include/ccimx8x.inc UBOOT_CONFIG ??= "ccimx8x_sbc_pro" UBOOT_CONFIG[ccimx8x_sbc_pro] = "ccimx8x_sbc_pro_defconfig,,u-boot-dtb.${UBOOT_SUFFIX}" SPL_BINARY = "spl/u-boot-spl.bin" +# Index of USB device for installer +UBOOT_INSTALL_USB_INDEX = "0" +# Index of microSD device for installer +UBOOT_INSTALL_SD_INDEX = "1" KERNEL_DEVICETREE ?= " \ digi/ccimx8x_bt.dtbo \ diff --git a/meta-digi-arm/conf/machine/ccimx91-dvk.conf b/meta-digi-arm/conf/machine/ccimx91-dvk.conf index 1a7f6ed4e..1dbc47989 100644 --- a/meta-digi-arm/conf/machine/ccimx91-dvk.conf +++ b/meta-digi-arm/conf/machine/ccimx91-dvk.conf @@ -14,6 +14,11 @@ IMXBOOT_TARGETS = "flash_singleboot" UBOOT_CONFIG ??= "ccimx91-dvk" UBOOT_CONFIG[ccimx91-dvk] = "ccimx91-dvk_defconfig" +# Index of USB device for installer +UBOOT_INSTALL_USB_INDEX = "0" +# Index of microSD device for installer +UBOOT_INSTALL_SD_INDEX = "1" + # imx-boot configurations ATF_PLATFORM = "imx91" IMX_BOOT_SOC_TARGET = "iMX91" diff --git a/meta-digi-arm/conf/machine/ccimx93-dvk.conf b/meta-digi-arm/conf/machine/ccimx93-dvk.conf index b30ccfc2d..b139b792d 100644 --- a/meta-digi-arm/conf/machine/ccimx93-dvk.conf +++ b/meta-digi-arm/conf/machine/ccimx93-dvk.conf @@ -12,6 +12,11 @@ include conf/machine/include/ccimx9.inc UBOOT_CONFIG ??= "ccimx93-dvk" UBOOT_CONFIG[ccimx93-dvk] = "ccimx93-dvk_defconfig" +# Index of USB device for installer +UBOOT_INSTALL_USB_INDEX = "0" +# Index of microSD device for installer +UBOOT_INSTALL_SD_INDEX = "1" + # imx-boot configurations ATF_PLATFORM = "imx93" IMX_BOOT_SOC_TARGET = "iMX93" diff --git a/meta-digi-arm/conf/machine/ccmp13-dvk.conf b/meta-digi-arm/conf/machine/ccmp13-dvk.conf index 00124a7bb..9302b5215 100644 --- a/meta-digi-arm/conf/machine/ccmp13-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp13-dvk.conf @@ -31,6 +31,10 @@ UBOOT_CONFIG[ccmp13-dvk] = "ccmp13-dvk_defconfig,,u-boot.dtb" UBOOT_CONFIG:remove:stm32mp13common = "default_stm32mp13" # List of U-Boot device tree to use UBOOT_DEVICETREE = "${STM32MP_DEVICETREE}" +# Index of USB device for installer +UBOOT_INSTALL_USB_INDEX = "0" +# Index of microSD device for installer +UBOOT_INSTALL_SD_INDEX = "1" # ========================================================================= # Machine settings diff --git a/meta-digi-arm/conf/machine/ccmp15-dvk.conf b/meta-digi-arm/conf/machine/ccmp15-dvk.conf index 072beb818..0462335ac 100644 --- a/meta-digi-arm/conf/machine/ccmp15-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp15-dvk.conf @@ -31,6 +31,10 @@ UBOOT_CONFIG[ccmp15-dvk] = "ccmp15-dvk_defconfig,,u-boot.dtb" UBOOT_CONFIG:remove:stm32mp15common = "default_stm32mp15" # List of U-Boot device tree to use UBOOT_DEVICETREE = "${STM32MP_DEVICETREE}" +# Index of USB device for installer +UBOOT_INSTALL_USB_INDEX = "0" +# Index of microSD device for installer +UBOOT_INSTALL_SD_INDEX = "1" # ========================================================================= # Machine settings diff --git a/meta-digi-arm/conf/machine/ccmp25-dvk.conf b/meta-digi-arm/conf/machine/ccmp25-dvk.conf index a6420f48a..124ec0a90 100644 --- a/meta-digi-arm/conf/machine/ccmp25-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp25-dvk.conf @@ -33,6 +33,10 @@ UBOOT_CONFIG = "ccmp25-dvk" UBOOT_CONFIG[ccmp25-dvk] = "ccmp25-dvk_defconfig,,u-boot.dtb" # List of U-Boot device tree to use UBOOT_DEVICETREE = "${STM32MP_DEVICETREE}" +# Index of USB device for installer +UBOOT_INSTALL_USB_INDEX = "0" +# Index of microSD device for installer +UBOOT_INSTALL_SD_INDEX = "2" # ========================================================================= # Machine settings diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc index 9c90fd148..fd75cc713 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc @@ -29,7 +29,8 @@ INSTALL_FW_UBOOT_SCRIPTS = " \ SRC_URI = " \ ${UBOOT_GIT_URI};branch=${SRCBRANCH} \ file://boot.txt \ - ${INSTALL_FW_UBOOT_SCRIPTS} \ + file://install_linux_fw_media.txt \ + file://install_linux_fw_uuu.sh \ " BUILD_UBOOT_SCRIPTS ?= "true" @@ -77,6 +78,15 @@ do_deploy[postfuncs] += " \ " build_uboot_scripts() { + # Generate USB installer from media template + sed -e 's,##INSTALL_MEDIA##,usb,g' \ + -e 's,##INSTALL_MEDIA_INDEX##,${UBOOT_INSTALL_USB_INDEX},g' \ + ${WORKDIR}/install_linux_fw_media.txt > ${WORKDIR}/install_linux_fw_usb.txt + # Generate microSD installer from media template + sed -e 's,##INSTALL_MEDIA##,mmc,g' \ + -e 's,##INSTALL_MEDIA_INDEX##,${UBOOT_INSTALL_SD_INDEX},g' \ + ${WORKDIR}/install_linux_fw_media.txt > ${WORKDIR}/install_linux_fw_sd.txt + for f in $(echo ${INSTALL_FW_UBOOT_SCRIPTS} | sed -e 's,file\:\/\/,,g'); do f_ext="${f##*.}" TMP_INSTALL_SCR="$(mktemp ${WORKDIR}/${f}.XXXXXX)" diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_media.txt similarity index 85% rename from meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_usb.txt rename to meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_media.txt index 57aa04d39..5b62ef222 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_media.txt @@ -1,5 +1,5 @@ # -# U-Boot script for installing Linux images created by Yocto from a USB stick +# U-Boot script for installing Linux images created by Yocto from external media # into the eMMC # @@ -37,7 +37,8 @@ else exit; fi; -setenv INSTALL_USBDEV 0 +setenv INSTALL_MEDIA_INDEX ##INSTALL_MEDIA_INDEX## +setenv INSTALL_MEDIA ##INSTALL_MEDIA## if test -z "${image-name}"; then setenv image-name ##DEFAULT_IMAGE_NAME## @@ -54,13 +55,13 @@ fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -# Check for presence of firmware files on the USB +# Check for presence of firmware files on the media FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" if test "${dualboot}" != "yes"; then FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" fi for install_f in ${FILES}; do - if test ! -e usb ${INSTALL_USBDEV} ${install_f}; then + if test ! -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${install_f}; then echo "ERROR: Could not find file ${install_f}"; install_abort=1; fi; @@ -68,9 +69,9 @@ done # Verify what kind of rootfs is going to be programmed ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" -if test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME}; then +if test -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${ROOTFS_FILENAME}; then setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; -elif test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME_SQFS}; then +elif test -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${ROOTFS_FILENAME_SQFS}; then setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; else echo "ERROR: Could not find any rootfs image"; @@ -84,7 +85,7 @@ if test "${install_abort}" = "1"; then fi echo "############################################################" -echo "# Linux firmware install from USB #" +echo "# Linux firmware install from media #" echo "############################################################" echo "" echo " This process will erase your eMMC and will install the following files" @@ -133,7 +134,7 @@ echo "" echo ">> Installing U-Boot boot loader image ${INSTALL_UBOOT_FILENAME} (target will reset)" echo "" echo "" -update uboot usb ${INSTALL_USBDEV} fat ${INSTALL_UBOOT_FILENAME} +update uboot ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} fat ${INSTALL_UBOOT_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update U-Boot boot loader!"; echo ""; @@ -172,14 +173,16 @@ setenv bootcmd " echo \"Aborted.\"; exit; fi; - usb start; + if test \"\$\{INSTALL_MEDIA\}\" = "usb"; then + usb start; + fi; if test \"\$\{dualboot\}\" = yes; then echo \"\"; echo \"\"; echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; echo \"\"; echo \"\"; - update linux_a usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux_a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_a!\"; echo \"\"; @@ -192,7 +195,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; echo \"\"; echo \"\"; - update linux_b usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux_b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_b!\"; echo \"\"; @@ -200,13 +203,15 @@ setenv bootcmd " exit; fi; fi; - usb reset; + if test \"\$\{INSTALL_MEDIA\}\" = "usb"; then + usb reset; + fi; echo \"\"; echo \"\"; echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; echo \"\"; echo \"\"; - update rootfs_a usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs_a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_a partition!\"; echo \"\"; @@ -219,7 +224,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; echo \"\"; echo \"\"; - update rootfs_b usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs_b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_b partition!\"; echo \"\"; @@ -234,7 +239,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; echo \"\"; echo \"\"; - update linux usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux!\"; echo \"\"; @@ -246,7 +251,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; echo \"\"; echo \"\"; - update recovery usb ${INSTALL_USBDEV} ${INSTALL_RECOVERY_FILENAME}; + update recovery ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_RECOVERY_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update recovery partition!\"; echo \"\"; @@ -258,7 +263,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; echo \"\"; echo \"\"; - update rootfs usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs partition!\"; echo \"\"; diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_sd.txt deleted file mode 100644 index a44d894d4..000000000 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6qpsbc/install_linux_fw_sd.txt +++ /dev/null @@ -1,285 +0,0 @@ -# -# U-Boot script for installing Linux images created by Yocto from the SD -# card into the eMMC -# - -# Reset temp variables -install_abort=0 -BASEFILENAME=0 - -# Determine U-Boot file to program basing on module variant -# If module_variant is unknown or not set, return error asking the user -if test "${module_variant}" = "0x01" || test "${module_variant}" = "0x02"; then - setenv INSTALL_UBOOT_FILENAME u-boot-##MACHINE##2GB.imx; -elif test "${module_variant}" = "0x03"; then - setenv INSTALL_UBOOT_FILENAME u-boot-##MACHINE##1GB.imx; -fi - -# Use 'test -n ...' because 'test -z ...' does not work well on old versions of -# u-boot when the checked value is empty. -if test -n "${INSTALL_UBOOT_FILENAME}"; then - true; -else - echo ""; - echo "[ERROR] Cannot determine U-Boot file for this module!"; - echo ""; - echo "1. Set variable 'INSTALL_UBOOT_FILENAME' depending on your ConnectCore 6 QuadPlus variant:"; - echo " - For a QuadPlus CPU with 2GB DDR3, run:"; - echo " => setenv INSTALL_UBOOT_FILENAME u-boot-##MACHINE##2GB.imx"; - echo " - For a DualPlus CPU with 1GB DDR3, run:"; - echo " => setenv INSTALL_UBOOT_FILENAME u-boot-##MACHINE##1GB.imx"; - echo ""; - echo ""; - echo "2. Run the install script again."; - echo ""; - echo "Aborted"; - echo ""; - exit; -fi; - -setenv INSTALL_MMCDEV 1 - -if test -z "${image-name}"; then - setenv image-name ##DEFAULT_IMAGE_NAME## -fi -GRAPHICAL_IMAGES="##GRAPHICAL_IMAGES##" -for g in ${GRAPHICAL_IMAGES}; do - if test "${image-name}" = "${g}"; then - BASEFILENAME="${image-name}-##GRAPHICAL_BACKEND##" - fi -done -if test "${BASEFILENAME}" = "0"; then - BASEFILENAME="${image-name}" -fi -setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat -setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat - -# Check for presence of firmware files on the SD card -FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" -if test "${dualboot}" != "yes"; then - FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" -fi -for install_f in ${FILES}; do - if test ! -e mmc ${INSTALL_MMCDEV} ${install_f}; then - echo "ERROR: Could not find file ${install_f}"; - install_abort=1; - fi; -done -# Verify what kind of rootfs is going to be programmed -ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" -ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" -if test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME}; then - setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; -elif test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME_SQFS}; then - setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; -else - echo "ERROR: Could not find any rootfs image"; - install_abort=1; -fi -if test "${install_abort}" = "1"; then - echo "To install a different image, set variable 'image-name', e.g.:" - echo " => setenv image-name core-image-base" - echo "Aborted."; - exit; -fi - -echo "############################################################" -echo "# Linux firmware install from micro SD #" -echo "############################################################" -echo "" -echo " This process will erase your eMMC and will install the following files" -echo " on the partitions of the eMMC." -echo "" -echo " PARTITION FILENAME" -echo " --------- --------" -echo " bootloader ${INSTALL_UBOOT_FILENAME}" -if test "${dualboot}" = "yes"; then - echo " linux_a ${INSTALL_LINUX_FILENAME}" - if test "${install_dualboot}" = "yes"; then - echo " linux_b ${INSTALL_LINUX_FILENAME}" - fi - echo " rootfs_a ${INSTALL_ROOTFS_FILENAME}" - if test "${install_dualboot}" = "yes"; then - echo " rootfs_b ${INSTALL_ROOTFS_FILENAME}" - fi -else - echo " linux ${INSTALL_LINUX_FILENAME}" - echo " recovery ${INSTALL_RECOVERY_FILENAME}" - echo " rootfs ${INSTALL_ROOTFS_FILENAME}" -fi -echo "" -echo " Press CTRL+C now if you wish to abort or wait 10 seconds" -echo " to continue." - -sleep 10 -if test $? -eq 1; then - echo "Aborted by user."; - exit; -fi - -# Skip user confirmation for U-Boot update -setenv forced_update 1 - -# Set bootdelay to zero so that firmware update is run immediately after -# the first reset. -setenv bootdelay 0 - -# Set target MMC device index to eMMC -setenv mmcdev 0 - -# Update U-Boot -echo "" -echo "" -echo ">> Installing U-Boot boot loader image ${INSTALL_UBOOT_FILENAME} (target will reset)" -echo "" -echo "" -update uboot mmc ${INSTALL_MMCDEV} fat ${INSTALL_UBOOT_FILENAME} -if test $? -eq 1; then - echo "[ERROR] Failed to update U-Boot boot loader!"; - echo ""; - echo "Aborted."; - exit; -fi - -# Set 'bootcmd' to the second part of the script that will -# - Reset environment to defaults -# - Restore 'dualboot' if previously set -# - Save the environment -# - Partition the eMMC user data area for Linux -# - If Dual Boot -# - Update the system partitions: linux_a, linux_b, rootfs_a, rootfs_b -# - Set bootlimit to 3 -# - If Normal Boot: -# - Update the system partitions: linux, recovery, rootfs -# - Erase the 'update' partition -# - Configure recovery to wipe 'update' partition -# - Run 'recovery' and let the system boot after -setenv bootcmd " - env default -a; - setenv dualboot ${dualboot}; - setenv install_dualboot ${install_dualboot}; - saveenv; - echo \"\"; - echo \"\"; - echo \">> Creating Linux partition table on the eMMC\"; - echo \"\"; - echo \"\"; - run partition_mmc_linux; - if test \$? -eq 1; then - echo \"[ERROR] Failed to create Linux partition table!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; - echo \"\"; - echo \"\"; - update linux_a mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux_a!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{install_dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; - echo \"\"; - echo \"\"; - update linux_b mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux_b!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; - echo \"\"; - echo \"\"; - update rootfs_a mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs_a partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{install_dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; - echo \"\"; - echo \"\"; - update rootfs_b mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs_b partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - fi; - setenv bootlimit 3; - else - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; - echo \"\"; - echo \"\"; - update linux mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; - echo \"\"; - echo \"\"; - update recovery mmc ${INSTALL_MMCDEV} ${INSTALL_RECOVERY_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update recovery partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; - echo \"\"; - echo \"\"; - update rootfs mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - setenv boot_recovery yes; - setenv recovery_command wipe_update; - fi; - setenv bootcount 0; - saveenv; - echo \"\"; - echo \"\"; - echo \">> Firmware installation complete.\"; - if test \"\$\{dualboot\}\" != yes; then - echo \"Rebooting into recovery mode for final deployment.\"; - fi; - echo \"\"; - echo \"\"; - sleep 1; - reset; -" - -setenv bootcount 0 -saveenv -reset diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_media.txt similarity index 86% rename from meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_usb.txt rename to meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_media.txt index 984df8e76..6fcefb035 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_media.txt @@ -1,5 +1,5 @@ # -# U-Boot script for installing Linux images created by Yocto from a USB stick +# U-Boot script for installing Linux images created by Yocto from external media # into the eMMC # @@ -44,7 +44,8 @@ else exit; fi; -setenv INSTALL_USBDEV 0 +setenv INSTALL_MEDIA_INDEX ##INSTALL_MEDIA_INDEX## +setenv INSTALL_MEDIA ##INSTALL_MEDIA## if test -z "${image-name}"; then setenv image-name ##DEFAULT_IMAGE_NAME## @@ -61,13 +62,13 @@ fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -# Check for presence of firmware files on the USB +# Check for presence of firmware files on the media FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" if test "${dualboot}" != "yes"; then FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" fi for install_f in ${FILES}; do - if test ! -e usb ${INSTALL_USBDEV} ${install_f}; then + if test ! -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${install_f}; then echo "ERROR: Could not find file ${install_f}"; install_abort=1; fi; @@ -75,9 +76,9 @@ done # Verify what kind of rootfs is going to be programmed ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" -if test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME}; then +if test -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${ROOTFS_FILENAME}; then setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; -elif test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME_SQFS}; then +elif test -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${ROOTFS_FILENAME_SQFS}; then setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; else echo "ERROR: Could not find any rootfs image"; @@ -91,7 +92,7 @@ if test "${install_abort}" = "1"; then fi echo "############################################################" -echo "# Linux firmware install from USB #" +echo "# Linux firmware install from media #" echo "############################################################" echo "" echo " This process will erase your eMMC and will install the following files" @@ -140,7 +141,7 @@ echo "" echo ">> Installing U-Boot boot loader image ${INSTALL_UBOOT_FILENAME} (target will reset)" echo "" echo "" -update uboot usb ${INSTALL_USBDEV} fat ${INSTALL_UBOOT_FILENAME} +update uboot ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} fat ${INSTALL_UBOOT_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update U-Boot boot loader!"; echo ""; @@ -179,14 +180,16 @@ setenv bootcmd " echo \"Aborted.\"; exit; fi; - usb start; + if test \"\$\{INSTALL_MEDIA\}\" = "usb"; then + usb start; + fi; if test \"\$\{dualboot\}\" = yes; then echo \"\"; echo \"\"; echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; echo \"\"; echo \"\"; - update linux_a usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux_a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_a!\"; echo \"\"; @@ -199,7 +202,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; echo \"\"; echo \"\"; - update linux_b usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux_b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_b!\"; echo \"\"; @@ -207,13 +210,15 @@ setenv bootcmd " exit; fi; fi; - usb reset; + if test \"\$\{INSTALL_MEDIA\}\" = "usb"; then + usb reset; + fi; echo \"\"; echo \"\"; echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; echo \"\"; echo \"\"; - update rootfs_a usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs_a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_a partition!\"; echo \"\"; @@ -226,7 +231,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; echo \"\"; echo \"\"; - update rootfs_b usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs_b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_b partition!\"; echo \"\"; @@ -241,7 +246,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; echo \"\"; echo \"\"; - update linux usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux!\"; echo \"\"; @@ -253,7 +258,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; echo \"\"; echo \"\"; - update recovery usb ${INSTALL_USBDEV} ${INSTALL_RECOVERY_FILENAME}; + update recovery ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_RECOVERY_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update recovery partition!\"; echo \"\"; @@ -265,7 +270,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; echo \"\"; echo \"\"; - update rootfs usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs partition!\"; echo \"\"; diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_sd.txt deleted file mode 100644 index 9d5e7a68e..000000000 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6sbc/install_linux_fw_sd.txt +++ /dev/null @@ -1,292 +0,0 @@ -# -# U-Boot script for installing Linux images created by Yocto from the SD -# card into the eMMC -# - -# Reset temp variables -install_abort=0 -BASEFILENAME=0 - -# Determine U-Boot file to program basing on module variant -# If module_variant is unknown or not set, return error asking the user -if test "${module_variant}" = "0x12"; then - setenv INSTALL_UBOOT_FILENAME u-boot-ccimx6qsbc2GB.imx; -elif test "${module_variant}" = "0x02" || test "${module_variant}" = "0x04" || test "${module_variant}" = "0x05" || test "${module_variant}" = "0x0b" || test "${module_variant}" = "0x11" || test "${module_variant}" = "0x14" || test "${module_variant}" = "0x15" || test "${module_variant}" = "0x16"; then - setenv INSTALL_UBOOT_FILENAME u-boot-cc${soc_family}sbc.imx; -elif test "${module_variant}" = "0x03" || test "${module_variant}" = "0x0c" || test "${module_variant}" = "0x0e" || test "${module_variant}" = "0x0f" || test "${module_variant}" = "0x13"; then - setenv INSTALL_UBOOT_FILENAME u-boot-cc${soc_family}sbc512MB.imx; -fi - -# Use 'test -n ...' because 'test -z ...' does not work well on old versions of -# u-boot when the checked value is empty. -if test -n "${INSTALL_UBOOT_FILENAME}"; then - true; -else - echo ""; - echo "[ERROR] Cannot determine U-Boot file for this module!"; - echo ""; - echo "1. Set variable 'INSTALL_UBOOT_FILENAME' depending on your ConnectCore 6 variant:"; - echo " - For a Quad/Dual CPU with 2GB DDR3, run:"; - echo " => setenv INSTALL_UBOOT_FILENAME u-boot-ccimx6qsbc2GB.imx"; - echo " - For a Quad/Dual CPU with 1GB DDR3, run:"; - echo " => setenv INSTALL_UBOOT_FILENAME u-boot-ccimx6qsbc.imx"; - echo " - For a Quad/Dual CPU with 512MB DDR3, run:"; - echo " => setenv INSTALL_UBOOT_FILENAME u-boot-ccimx6qsbc512MB.imx"; - echo " - For a DualLite/Solo CPU with 1GB DDR3, run:"; - echo " => setenv INSTALL_UBOOT_FILENAME u-boot-ccimx6dlsbc.imx"; - echo " - For a DualLite/Solo CPU with 512MB DDR3, run:"; - echo " => setenv INSTALL_UBOOT_FILENAME u-boot-ccimx6dlsbc512MB.imx"; - echo ""; - echo "2. Run the install script again."; - echo ""; - echo "Aborted"; - echo ""; - exit; -fi; - -setenv INSTALL_MMCDEV 1 - -if test -z "${image-name}"; then - setenv image-name ##DEFAULT_IMAGE_NAME## -fi -GRAPHICAL_IMAGES="##GRAPHICAL_IMAGES##" -for g in ${GRAPHICAL_IMAGES}; do - if test "${image-name}" = "${g}"; then - BASEFILENAME="${image-name}-##GRAPHICAL_BACKEND##" - fi -done -if test "${BASEFILENAME}" = "0"; then - BASEFILENAME="${image-name}" -fi -setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat -setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat - -# Check for presence of firmware files on the SD card -FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" -if test "${dualboot}" != "yes"; then - FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" -fi -for install_f in ${FILES}; do - if test ! -e mmc ${INSTALL_MMCDEV} ${install_f}; then - echo "ERROR: Could not find file ${install_f}"; - install_abort=1; - fi; -done -# Verify what kind of rootfs is going to be programmed -ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" -ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" -if test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME}; then - setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; -elif test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME_SQFS}; then - setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; -else - echo "ERROR: Could not find any rootfs image"; - install_abort=1; -fi -if test "${install_abort}" = "1"; then - echo "To install a different image, set variable 'image-name', e.g.:" - echo " => setenv image-name core-image-base" - echo "Aborted."; - exit; -fi - -echo "############################################################" -echo "# Linux firmware install from micro SD #" -echo "############################################################" -echo "" -echo " This process will erase your eMMC and will install the following files" -echo " on the partitions of the eMMC." -echo "" -echo " PARTITION FILENAME" -echo " --------- --------" -echo " bootloader ${INSTALL_UBOOT_FILENAME}" -if test "${dualboot}" = "yes"; then - echo " linux_a ${INSTALL_LINUX_FILENAME}" - if test "${install_dualboot}" = "yes"; then - echo " linux_b ${INSTALL_LINUX_FILENAME}" - fi - echo " rootfs_a ${INSTALL_ROOTFS_FILENAME}" - if test "${install_dualboot}" = "yes"; then - echo " rootfs_b ${INSTALL_ROOTFS_FILENAME}" - fi -else - echo " linux ${INSTALL_LINUX_FILENAME}" - echo " recovery ${INSTALL_RECOVERY_FILENAME}" - echo " rootfs ${INSTALL_ROOTFS_FILENAME}" -fi -echo "" -echo " Press CTRL+C now if you wish to abort or wait 10 seconds" -echo " to continue." - -sleep 10 -if test $? -eq 1; then - echo "Aborted by user."; - exit; -fi - -# Skip user confirmation for U-Boot update -setenv forced_update 1 - -# Set bootdelay to zero so that firmware update is run immediately after -# the first reset. -setenv bootdelay 0 - -# Set target MMC device index to eMMC -setenv mmcdev 0 - -# Update U-Boot -echo "" -echo "" -echo ">> Installing U-Boot boot loader image ${INSTALL_UBOOT_FILENAME} (target will reset)" -echo "" -echo "" -update uboot mmc ${INSTALL_MMCDEV} fat ${INSTALL_UBOOT_FILENAME} -if test $? -eq 1; then - echo "[ERROR] Failed to update U-Boot boot loader!"; - echo ""; - echo "Aborted."; - exit; -fi - -# Set 'bootcmd' to the second part of the script that will -# - Reset environment to defaults -# - Restore 'dualboot' if previously set -# - Save the environment -# - Partition the eMMC user data area for Linux -# - If Dual Boot -# - Update the system partitions: linux_a, linux_b, rootfs_a, rootfs_b -# - Set bootlimit to 3 -# - If Normal Boot: -# - Update the system partitions: linux, recovery, rootfs -# - Erase the 'update' partition -# - Configure recovery to wipe 'update' partition -# - Run 'recovery' and let the system boot after -setenv bootcmd " - env default -a; - setenv dualboot ${dualboot}; - setenv install_dualboot ${install_dualboot}; - saveenv; - echo \"\"; - echo \"\"; - echo \">> Creating Linux partition table on the eMMC\"; - echo \"\"; - echo \"\"; - run partition_mmc_linux; - if test \$? -eq 1; then - echo \"[ERROR] Failed to create Linux partition table!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; - echo \"\"; - echo \"\"; - update linux_a mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux_a!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{install_dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; - echo \"\"; - echo \"\"; - update linux_b mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux_b!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; - echo \"\"; - echo \"\"; - update rootfs_a mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs_a partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{install_dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; - echo \"\"; - echo \"\"; - update rootfs_b mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs_b partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - fi; - setenv bootlimit 3; - else - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; - echo \"\"; - echo \"\"; - update linux mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; - echo \"\"; - echo \"\"; - update recovery mmc ${INSTALL_MMCDEV} ${INSTALL_RECOVERY_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update recovery partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; - echo \"\"; - echo \"\"; - update rootfs mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - setenv boot_recovery yes; - setenv recovery_command wipe_update; - fi; - setenv bootcount 0; - saveenv; - echo \"\"; - echo \"\"; - echo \">> Firmware installation complete.\"; - if test \"\$\{dualboot\}\" != yes; then - echo \"Rebooting into recovery mode for final deployment.\"; - fi; - echo \"\"; - echo \"\"; - sleep 1; - reset; -" - -setenv bootcount 0 -saveenv -reset diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_media.txt similarity index 85% rename from meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_usb.txt rename to meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_media.txt index 7b20779ea..e18af43ff 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_media.txt @@ -1,5 +1,5 @@ # -# U-Boot script for installing Linux images created by Yocto from a USB stick +# U-Boot script for installing Linux images created by Yocto from external media # into the NAND # @@ -17,6 +17,9 @@ elif test "${module_variant}" = "0x08" || test "${module_variant}" = "0x0a"; the setenv INSTALL_UBOOT_FILENAME u-boot-##MACHINE##512MB.imx fi +setenv INSTALL_MEDIA_INDEX ##INSTALL_MEDIA_INDEX## +setenv INSTALL_MEDIA ##INSTALL_MEDIA## + # Use 'test -n ...' because 'test -z ...' does not work well on old versions of # u-boot when the checked value is empty. if test -n "${INSTALL_UBOOT_FILENAME}"; then @@ -40,8 +43,6 @@ else exit; fi; -setenv INSTALL_USBDEV 0 - if test -z "${image-name}"; then setenv image-name ##DEFAULT_IMAGE_NAME## fi @@ -63,7 +64,7 @@ if test "${dualboot}" != "yes"; then FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" fi for install_f in ${FILES}; do - if test ! -e usb ${INSTALL_USBDEV} ${install_f}; then + if test ! -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${install_f}; then echo "ERROR: Could not find file ${install_f}"; install_abort=1; fi; @@ -71,10 +72,10 @@ done # Verify what kind of rootfs is going to be programmed ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ubifs" ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" -if test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME}; then +if test -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${ROOTFS_FILENAME}; then setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; setenv rootfstype; -elif test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME_SQFS}; then +elif test -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${ROOTFS_FILENAME_SQFS}; then setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; setenv rootfstype squashfs; else @@ -89,7 +90,7 @@ if test "${install_abort}" = "1"; then fi echo "############################################################" -echo "# Linux firmware install from USB #" +echo "# Linux firmware install from media #" echo "############################################################" echo "" echo " This process will erase your NAND and will install the following files" @@ -135,10 +136,10 @@ echo "" echo ">> Installing U-Boot boot loader image ${INSTALL_UBOOT_FILENAME} (target will reset)" echo "" echo "" -update uboot usb ${INSTALL_USBDEV} ${INSTALL_UBOOT_FILENAME} +update uboot ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_UBOOT_FILENAME} if test $? -eq 1; then # Use old-style update with source file system argument - update uboot usb ${INSTALL_USBDEV} fat ${INSTALL_UBOOT_FILENAME} + update uboot ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} fat ${INSTALL_UBOOT_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update U-Boot boot loader!"; echo ""; @@ -173,14 +174,16 @@ setenv bootcmd " else force_erase=\"-e\"; fi; - usb start; + if test \"\$\{INSTALL_MEDIA\}\" = "usb"; then + usb start; + fi; if test \"\$\{dualboot\}\" = yes; then echo \"\"; echo \"\"; echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; echo \"\"; echo \"\"; - update linux_a usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME} \$\{force_erase\}; + update linux_a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_a!\"; echo \"\"; @@ -193,7 +196,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; echo \"\"; echo \"\"; - update linux_b usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME} \$\{force_erase\}; + update linux_b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_b!\"; echo \"\"; @@ -201,12 +204,15 @@ setenv bootcmd " exit; fi; fi; + if test \"\$\{INSTALL_MEDIA\}\" = "usb"; then + usb reset; + fi; echo \"\"; echo \"\"; echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; echo \"\"; echo \"\"; - update rootfs_a usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME} \$\{force_erase\}; + update rootfs_a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_a partition!\"; echo \"\"; @@ -219,7 +225,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; echo \"\"; echo \"\"; - update rootfs_b usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME} \$\{force_erase\}; + update rootfs_b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_b partition!\"; echo \"\"; @@ -234,7 +240,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; echo \"\"; echo \"\"; - update linux usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME} \$\{force_erase\}; + update linux ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux!\"; echo \"\"; @@ -246,7 +252,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; echo \"\"; echo \"\"; - update recovery usb ${INSTALL_USBDEV} ${INSTALL_RECOVERY_FILENAME} \$\{force_erase\}; + update recovery ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_RECOVERY_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update recovery partition!\"; echo \"\"; @@ -258,7 +264,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; echo \"\"; echo \"\"; - update rootfs usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME} \$\{force_erase\}; + update rootfs ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs partition!\"; echo \"\"; diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_sd.txt deleted file mode 100644 index d4cb888a4..000000000 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ul/install_linux_fw_sd.txt +++ /dev/null @@ -1,290 +0,0 @@ -# -# U-Boot script for installing Linux images created by Yocto from the micro SD -# card into the NAND -# - -# Reset temp variables -install_abort=0 -BASEFILENAME=0 - -# Determine U-Boot file to program basing on module variant -# If module_variant is unknown or not set, return error asking the user -if test "${module_variant}" = "0x02" || test "${module_variant}" = "0x03" || test "${module_variant}" = "0x06" || test "${module_variant}" = "0x09"; then - setenv INSTALL_UBOOT_FILENAME u-boot-##MACHINE##.imx; -elif test "${module_variant}" = "0x04" || test "${module_variant}" = "0x05" || test "${module_variant}" = "0x07"; then - setenv INSTALL_UBOOT_FILENAME u-boot-##MACHINE##1GB.imx; -elif test "${module_variant}" = "0x08" || test "${module_variant}" = "0x0a"; then - setenv INSTALL_UBOOT_FILENAME u-boot-##MACHINE##512MB.imx -fi - -# Use 'test -n ...' because 'test -z ...' does not work well on old versions of -# u-boot when the checked value is empty. -if test -n "${INSTALL_UBOOT_FILENAME}"; then - true; -else - echo ""; - echo "[ERROR] Cannot determine U-Boot file for this module!"; - echo ""; - echo "1. Set variable 'INSTALL_UBOOT_FILENAME' depending on your ConnectCore 6UL variant:"; - echo " - For a SOM with 1GB DDR3, run:"; - echo " => setenv INSTALL_UBOOT_FILENAME u-boot-##MACHINE##1GB.imx"; - echo " - For a SOM with 512MB DDR3, run:"; - echo " => setenv INSTALL_UBOOT_FILENAME u-boot-##MACHINE##512MB.imx"; - echo " - For a SOM with 256MB DDR3, run:"; - echo " => setenv INSTALL_UBOOT_FILENAME u-boot-##MACHINE##.imx"; - echo ""; - echo "2. Run the install script again."; - echo ""; - echo "Aborted"; - echo ""; - exit; -fi; - -setenv INSTALL_MMCDEV 0 - -if test -z "${image-name}"; then - setenv image-name ##DEFAULT_IMAGE_NAME## -fi -GRAPHICAL_IMAGES="##GRAPHICAL_IMAGES##" -for g in ${GRAPHICAL_IMAGES}; do - if test "${image-name}" = "${g}"; then - BASEFILENAME="${image-name}-##GRAPHICAL_BACKEND##" - fi -done -if test "${BASEFILENAME}" = "0"; then - BASEFILENAME="${image-name}" -fi -setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.ubifs -setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.ubifs - -# Check for presence of firmware files on the SD card -FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" -if test "${dualboot}" != "yes"; then - FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" -fi -for install_f in ${FILES}; do - if test ! -e mmc ${INSTALL_MMCDEV} ${install_f}; then - echo "ERROR: Could not find file ${install_f}"; - install_abort=1; - fi; -done -# Verify what kind of rootfs is going to be programmed -ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ubifs" -ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" -if test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME}; then - setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; - setenv rootfstype; -elif test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME_SQFS}; then - setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; - setenv rootfstype squashfs; -else - echo "ERROR: Could not find any rootfs image"; - install_abort=1; -fi -if test "${install_abort}" = "1"; then - echo "To install a different image, set variable 'image-name', e.g.:" - echo " => setenv image-name core-image-base" - echo "Aborted."; - exit; -fi - -echo "############################################################" -echo "# Linux firmware install from micro SD #" -echo "############################################################" -echo "" -echo " This process will erase your NAND and will install the following files" -echo " on the partitions of the NAND." -echo "" -echo " PARTITION FILENAME" -echo " --------- --------" -echo " bootloader ${INSTALL_UBOOT_FILENAME}" -if test "${dualboot}" = "yes"; then - echo " linux_a ${INSTALL_LINUX_FILENAME}" - if test "${install_dualboot}" = "yes"; then - echo " linux_b ${INSTALL_LINUX_FILENAME}" - fi - echo " rootfs_a ${INSTALL_ROOTFS_FILENAME}" - if test "${install_dualboot}" = "yes"; then - echo " rootfs_b ${INSTALL_ROOTFS_FILENAME}" - fi -else - echo " linux ${INSTALL_LINUX_FILENAME}" - echo " recovery ${INSTALL_RECOVERY_FILENAME}" - echo " rootfs ${INSTALL_ROOTFS_FILENAME}" -fi -echo "" -echo " Press CTRL+C now if you wish to abort or wait 10 seconds" -echo " to continue." - -sleep 10 -if test $? -eq 1; then - echo "Aborted by user."; - exit; -fi - -# Skip user confirmation for U-Boot update -setenv forced_update 1 - -# Set bootdelay to zero so that firmware update is run immediately after -# the first reset. -setenv bootdelay 0 - -# Update U-Boot -echo "" -echo "" -echo ">> Installing U-Boot boot loader image ${INSTALL_UBOOT_FILENAME} (target will reset)" -echo "" -echo "" -update uboot mmc ${INSTALL_MMCDEV} ${INSTALL_UBOOT_FILENAME} -if test $? -eq 1; then - # Use old-style update with source file system argument - update uboot mmc ${INSTALL_MMCDEV} fat ${INSTALL_UBOOT_FILENAME} - if test $? -eq 1; then - echo "[ERROR] Failed to update U-Boot boot loader!"; - echo ""; - echo "Aborted."; - exit; - fi -fi - -# Set 'bootcmd' to the second part of the script that will -# - Reset environment to defaults -# - Restore 'singlemtdsys' and 'dualboot' if previously set -# - Run 'partition_nand_linux' script to re-partition the NAND if needed -# - Save the environment -# - If Dual Boot -# - Update the system partitions: linux_a, linux_b, rootfs_a, rootfs_b -# - Set bootlimit to 3 -# - If Normal Boot: -# - Update the system partitions: linux, recovery, rootfs -# - Erase the 'update' partition -# - Configure recovery to wipe 'update' partition -# - Run 'recovery' and let the system boot after -setenv bootcmd " - env default -a; - setenv singlemtdsys ${singlemtdsys}; - setenv dualboot ${dualboot}; - setenv rootfstype ${rootfstype}; - setenv install_dualboot ${install_dualboot}; - run partition_nand_linux; - saveenv; - if test \"\$\{singlemtdsys\}\" = yes; then - run ubivolscript; - else - force_erase=\"-e\"; - fi; - if test \"\$\{dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; - echo \"\"; - echo \"\"; - update linux_a mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME} \$\{force_erase\}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux_a!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{install_dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; - echo \"\"; - echo \"\"; - update linux_b mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME} \$\{force_erase\}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux_b!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; - echo \"\"; - echo \"\"; - update rootfs_a mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME} \$\{force_erase\}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs_a partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{install_dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; - echo \"\"; - echo \"\"; - update rootfs_b mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME} \$\{force_erase\}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs_b partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - fi; - setenv bootlimit 3; - else - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; - echo \"\"; - echo \"\"; - update linux mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME} \$\{force_erase\}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; - echo \"\"; - echo \"\"; - update recovery mmc ${INSTALL_MMCDEV} ${INSTALL_RECOVERY_FILENAME} \$\{force_erase\}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update recovery partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; - echo \"\"; - echo \"\"; - update rootfs mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME} \$\{force_erase\}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - if test \"\$\{singlemtdsys\}\" != yes; then - nand erase.part update; - fi; - setenv boot_recovery yes; - setenv recovery_command wipe_update; - fi; - saveenv; - echo \"\"; - echo \"\"; - echo \">> Firmware installation complete.\"; - if test \"\$\{dualboot\}\" != yes; then - echo \"Rebooting into recovery mode for final deployment.\"; - fi; - echo \"\"; - echo \"\"; - sleep 1; - bootcount reset; - reset; -" - -bootcount reset -saveenv -reset diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_media.txt similarity index 81% rename from meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_usb.txt rename to meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_media.txt index 338fc7fd8..cdde62c74 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_media.txt @@ -1,5 +1,5 @@ # -# U-Boot script for installing Linux images created by Yocto from a USB stick +# U-Boot script for installing Linux images created by Yocto from external media # into the eMMC # @@ -7,8 +7,8 @@ install_abort=0 BASEFILENAME=0 -setenv INSTALL_UBOOT_FILENAME imx-boot-##MACHINE##.bin; -setenv INSTALL_USBDEV 0 +setenv INSTALL_MEDIA_INDEX ##INSTALL_MEDIA_INDEX## +setenv INSTALL_MEDIA ##INSTALL_MEDIA## if test -z "${image-name}"; then setenv image-name ##DEFAULT_IMAGE_NAME## @@ -25,13 +25,13 @@ fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -# Check for presence of firmware files on the USB +# Check for presence of firmware files on the media FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" if test "${dualboot}" != "yes"; then FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" fi for install_f in ${FILES}; do - if test ! -e usb ${INSTALL_USBDEV} ${install_f}; then + if test ! -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${install_f}; then echo "ERROR: Could not find file ${install_f}"; install_abort=1; fi; @@ -39,9 +39,9 @@ done # Verify what kind of rootfs is going to be programmed ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" -if test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME}; then +if test -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${ROOTFS_FILENAME}; then setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; -elif test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME_SQFS}; then +elif test -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${ROOTFS_FILENAME_SQFS}; then setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; else echo "ERROR: Could not find any rootfs image"; @@ -55,7 +55,7 @@ if test "${install_abort}" = "1"; then fi echo "############################################################" -echo "# Linux firmware install from USB #" +echo "# Linux firmware install from media #" echo "############################################################" echo "" echo " This process will erase your eMMC and will install the following files" @@ -104,10 +104,10 @@ echo "" echo ">> Installing U-Boot boot loader image ${INSTALL_UBOOT_FILENAME} (target will reset)" echo "" echo "" -update uboot usb ${INSTALL_USBDEV} ${INSTALL_UBOOT_FILENAME} +update uboot ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} fat ${INSTALL_UBOOT_FILENAME} if test $? -eq 1; then # Use old-style update with source file system argument - update uboot usb ${INSTALL_USBDEV} fat ${INSTALL_UBOOT_FILENAME} + update uboot ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} fat ${INSTALL_UBOOT_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update U-Boot boot loader!"; echo ""; @@ -130,6 +130,7 @@ fi # - Erase the 'update' partition # - Configure recovery to wipe 'update' partition # - Run 'recovery' and let the system boot after +# NOTE: insert some 'usb reset' commands to avoid EHCI timeouts setenv bootcmd " env default -a; setenv dualboot ${dualboot}; @@ -148,14 +149,16 @@ setenv bootcmd " echo \"Aborted.\"; exit; fi; - usb start; + if test \"\$\{INSTALL_MEDIA\}\" = "usb"; then + usb start; + fi; if test \"\$\{dualboot\}\" = yes; then echo \"\"; echo \"\"; echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; echo \"\"; echo \"\"; - update linux_a usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux_a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_a!\"; echo \"\"; @@ -168,7 +171,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; echo \"\"; echo \"\"; - update linux_b usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux_b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_b!\"; echo \"\"; @@ -176,12 +179,15 @@ setenv bootcmd " exit; fi; fi; + if test \"\$\{INSTALL_MEDIA\}\" = "usb"; then + usb reset; + fi; echo \"\"; echo \"\"; echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; echo \"\"; echo \"\"; - update rootfs_a usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs_a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_a partition!\"; echo \"\"; @@ -194,7 +200,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; echo \"\"; echo \"\"; - update rootfs_b usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs_b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_b partition!\"; echo \"\"; @@ -209,7 +215,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; echo \"\"; echo \"\"; - update linux usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux!\"; echo \"\"; @@ -221,7 +227,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; echo \"\"; echo \"\"; - update recovery usb ${INSTALL_USBDEV} ${INSTALL_RECOVERY_FILENAME}; + update recovery ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_RECOVERY_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update recovery partition!\"; echo \"\"; @@ -233,7 +239,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; echo \"\"; echo \"\"; - update rootfs usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs partition!\"; echo \"\"; diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_sd.txt deleted file mode 100644 index 4d2b76809..000000000 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8m/install_linux_fw_sd.txt +++ /dev/null @@ -1,263 +0,0 @@ -# -# U-Boot script for installing Linux images created by Yocto from the SD -# card into the eMMC -# - -# Reset temp variables -install_abort=0 -BASEFILENAME=0 - -setenv INSTALL_UBOOT_FILENAME imx-boot-##MACHINE##.bin; -setenv INSTALL_MMCDEV 1 - -if test -z "${image-name}"; then - setenv image-name ##DEFAULT_IMAGE_NAME## -fi -GRAPHICAL_IMAGES="##GRAPHICAL_IMAGES##" -for g in ${GRAPHICAL_IMAGES}; do - if test "${image-name}" = "${g}"; then - BASEFILENAME="${image-name}-##GRAPHICAL_BACKEND##" - fi -done -if test "${BASEFILENAME}" = "0"; then - BASEFILENAME="${image-name}" -fi -setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat -setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat - -# Check for presence of firmware files on the SD card -FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" -if test "${dualboot}" != "yes"; then - FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" -fi -for install_f in ${FILES}; do - if test ! -e mmc ${INSTALL_MMCDEV} ${install_f}; then - echo "ERROR: Could not find file ${install_f}"; - install_abort=1; - fi; -done -# Verify what kind of rootfs is going to be programmed -ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" -ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" -if test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME}; then - setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; -elif test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME_SQFS}; then - setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; -else - echo "ERROR: Could not find any rootfs image"; - install_abort=1; -fi -if test "${install_abort}" = "1"; then - echo "To install a different image, set variable 'image-name', e.g.:" - echo " => setenv image-name core-image-base" - echo "Aborted."; - exit; -fi - -echo "############################################################" -echo "# Linux firmware install from micro SD #" -echo "############################################################" -echo "" -echo " This process will erase your eMMC and will install the following files" -echo " on the partitions of the eMMC." -echo "" -echo " PARTITION FILENAME" -echo " --------- --------" -echo " bootloader ${INSTALL_UBOOT_FILENAME}" -if test "${dualboot}" = "yes"; then - echo " linux_a ${INSTALL_LINUX_FILENAME}" - if test "${install_dualboot}" = "yes"; then - echo " linux_b ${INSTALL_LINUX_FILENAME}" - fi - echo " rootfs_a ${INSTALL_ROOTFS_FILENAME}" - if test "${install_dualboot}" = "yes"; then - echo " rootfs_b ${INSTALL_ROOTFS_FILENAME}" - fi -else - echo " linux ${INSTALL_LINUX_FILENAME}" - echo " recovery ${INSTALL_RECOVERY_FILENAME}" - echo " rootfs ${INSTALL_ROOTFS_FILENAME}" -fi -echo "" -echo " Press CTRL+C now if you wish to abort or wait 10 seconds" -echo " to continue." - -sleep 10 -if test $? -eq 1; then - echo "Aborted by user."; - exit; -fi - -# Skip user confirmation for U-Boot update -setenv forced_update 1 - -# Set bootdelay to zero so that firmware update is run immediately after -# the first reset. -setenv bootdelay 0 - -# Set target MMC device index to eMMC -setenv mmcdev 0 - -# Update U-Boot -echo "" -echo "" -echo ">> Installing U-Boot boot loader image ${INSTALL_UBOOT_FILENAME} (target will reset)" -echo "" -echo "" -update uboot mmc ${INSTALL_MMCDEV} ${INSTALL_UBOOT_FILENAME} -if test $? -eq 1; then - # Use old-style update with source file system argument - update uboot mmc ${INSTALL_MMCDEV} fat ${INSTALL_UBOOT_FILENAME} - if test $? -eq 1; then - echo "[ERROR] Failed to update U-Boot boot loader!"; - echo ""; - echo "Aborted."; - exit; - fi -fi - -# Set 'bootcmd' to the second part of the script that will -# - Reset environment to defaults -# - Restore 'dualboot' if previously set -# - Save the environment -# - Force on-the-fly updates to avoid possible verification errors -# - Partition the eMMC user data area for Linux -# - If Dual Boot -# - Update the system partitions: linux_a, linux_b, rootfs_a, rootfs_b -# - Set bootlimit to 3 -# - If Normal Boot: -# - Update the system partitions: linux, recovery, rootfs -# - Erase the 'update' partition -# - Configure recovery to wipe 'update' partition -# - Run 'recovery' and let the system boot after -setenv bootcmd " - env default -a; - setenv dualboot ${dualboot}; - setenv install_dualboot ${install_dualboot}; - saveenv; - setenv otf-update yes; - echo \"\"; - echo \"\"; - echo \">> Creating Linux partition table on the eMMC\"; - echo \"\"; - echo \"\"; - run partition_mmc_linux; - if test \$? -eq 1; then - echo \"[ERROR] Failed to create Linux partition table!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; - echo \"\"; - echo \"\"; - update linux_a mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux_a!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{install_dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; - echo \"\"; - echo \"\"; - update linux_b mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux_b!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; - echo \"\"; - echo \"\"; - update rootfs_a mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs_a partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{install_dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; - echo \"\"; - echo \"\"; - update rootfs_b mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs_b partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - fi; - setenv bootlimit 3; - else - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; - echo \"\"; - echo \"\"; - update linux mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; - echo \"\"; - echo \"\"; - update recovery mmc ${INSTALL_MMCDEV} ${INSTALL_RECOVERY_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update recovery partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; - echo \"\"; - echo \"\"; - update rootfs mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - setenv boot_recovery yes; - setenv recovery_command wipe_update; - fi; - setenv otf-update; - saveenv; - echo \"\"; - echo \"\"; - echo \">> Firmware installation complete.\"; - if test \"\$\{dualboot\}\" != yes; then - echo \"Rebooting into recovery mode for final deployment.\"; - fi; - echo \"\"; - echo \"\"; - sleep 1; - bootcount reset; - reset; -" - -bootcount reset -saveenv -reset diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_media.txt similarity index 83% rename from meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_usb.txt rename to meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_media.txt index 9dd742845..5fb16dbda 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_media.txt @@ -1,5 +1,5 @@ # -# U-Boot script for installing Linux images created by Yocto from a USB stick +# U-Boot script for installing Linux images created by Yocto from external media # into the eMMC # @@ -18,9 +18,8 @@ fi # the necessary logic to choose the correct imx-boot file for the target's SOC. setenv skip-uboot-check "yes" -setenv INSTALL_UBOOT_FILENAME imx-boot-##MACHINE##-${soc_rev}.bin; - -setenv INSTALL_USBDEV 0 +setenv INSTALL_MEDIA_INDEX ##INSTALL_MEDIA_INDEX## +setenv INSTALL_MEDIA ##INSTALL_MEDIA## if test -z "${image-name}"; then setenv image-name ##DEFAULT_IMAGE_NAME## @@ -37,13 +36,13 @@ fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -# Check for presence of firmware files on the USB +# Check for presence of firmware files on the media FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" if test "${dualboot}" != "yes"; then FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" fi for install_f in ${FILES}; do - if test ! -e usb ${INSTALL_USBDEV} ${install_f}; then + if test ! -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${install_f}; then echo "ERROR: Could not find file ${install_f}"; install_abort=1; fi; @@ -51,9 +50,9 @@ done # Verify what kind of rootfs is going to be programmed ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" -if test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME}; then +if test -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${ROOTFS_FILENAME}; then setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; -elif test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME_SQFS}; then +elif test -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${ROOTFS_FILENAME_SQFS}; then setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; else echo "ERROR: Could not find any rootfs image"; @@ -67,7 +66,7 @@ if test "${install_abort}" = "1"; then fi echo "############################################################" -echo "# Linux firmware install from USB #" +echo "# Linux firmware install from media #" echo "############################################################" echo "" echo " This process will erase your eMMC and will install the following files" @@ -116,10 +115,10 @@ echo "" echo ">> Installing U-Boot boot loader image ${INSTALL_UBOOT_FILENAME} (target will reset)" echo "" echo "" -update uboot usb ${INSTALL_USBDEV} ${INSTALL_UBOOT_FILENAME} +update uboot ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} fat ${INSTALL_UBOOT_FILENAME} if test $? -eq 1; then # Use old-style update with source file system argument - update uboot usb ${INSTALL_USBDEV} fat ${INSTALL_UBOOT_FILENAME} + update uboot ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} fat ${INSTALL_UBOOT_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update U-Boot boot loader!"; echo ""; @@ -161,14 +160,16 @@ setenv bootcmd " echo \"Aborted.\"; exit; fi; - usb start; + if test \"\$\{INSTALL_MEDIA\}\" = "usb"; then + usb start; + fi; if test \"\$\{dualboot\}\" = yes; then echo \"\"; echo \"\"; echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; echo \"\"; echo \"\"; - update linux_a usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux_a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_a!\"; echo \"\"; @@ -181,7 +182,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; echo \"\"; echo \"\"; - update linux_b usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux_b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_b!\"; echo \"\"; @@ -189,13 +190,15 @@ setenv bootcmd " exit; fi; fi; - usb reset; + if test \"\$\{INSTALL_MEDIA\}\" = "usb"; then + usb reset; + fi; echo \"\"; echo \"\"; echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; echo \"\"; echo \"\"; - update rootfs_a usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs_a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_a partition!\"; echo \"\"; @@ -208,7 +211,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; echo \"\"; echo \"\"; - update rootfs_b usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs_b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_b partition!\"; echo \"\"; @@ -223,7 +226,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; echo \"\"; echo \"\"; - update linux usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux!\"; echo \"\"; @@ -235,7 +238,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; echo \"\"; echo \"\"; - update recovery usb ${INSTALL_USBDEV} ${INSTALL_RECOVERY_FILENAME}; + update recovery ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_RECOVERY_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update recovery partition!\"; echo \"\"; @@ -247,7 +250,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; echo \"\"; echo \"\"; - update rootfs usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs partition!\"; echo \"\"; diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_sd.txt deleted file mode 100644 index 1c777d062..000000000 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx8x/install_linux_fw_sd.txt +++ /dev/null @@ -1,275 +0,0 @@ -# -# U-Boot script for installing Linux images created by Yocto from the SD -# card into the eMMC -# - -# Reset temp variables -install_abort=0 -BASEFILENAME=0 - -# Since SOMs with the B0 SOC might have an older U-Boot that doesn't export the -# SOC revision to the environment, use B0 by default -if test -z "${soc_rev}"; then - setenv soc_rev B0 -fi - -# In case this script is used to update to a newer release whose imx-boot file -# is not recognized by the SECO fw check, bypass it by default. The script has -# the necessary logic to choose the correct imx-boot file for the target's SOC. -setenv skip-uboot-check "yes" - -setenv INSTALL_UBOOT_FILENAME imx-boot-##MACHINE##-${soc_rev}.bin; - -setenv INSTALL_MMCDEV 1 - -if test -z "${image-name}"; then - setenv image-name ##DEFAULT_IMAGE_NAME## -fi -GRAPHICAL_IMAGES="##GRAPHICAL_IMAGES##" -for g in ${GRAPHICAL_IMAGES}; do - if test "${image-name}" = "${g}"; then - BASEFILENAME="${image-name}-##GRAPHICAL_BACKEND##" - fi -done -if test "${BASEFILENAME}" = "0"; then - BASEFILENAME="${image-name}" -fi -setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat -setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat - -# Check for presence of firmware files on the SD card -FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" -if test "${dualboot}" != "yes"; then - FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" -fi -for install_f in ${FILES}; do - if test ! -e mmc ${INSTALL_MMCDEV} ${install_f}; then - echo "ERROR: Could not find file ${install_f}"; - install_abort=1; - fi; -done -# Verify what kind of rootfs is going to be programmed -ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" -ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" -if test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME}; then - setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; -elif test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME_SQFS}; then - setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; -else - echo "ERROR: Could not find any rootfs image"; - install_abort=1; -fi -if test "${install_abort}" = "1"; then - echo "To install a different image, set variable 'image-name', e.g.:" - echo " => setenv image-name core-image-base" - echo "Aborted."; - exit; -fi - -echo "############################################################" -echo "# Linux firmware install from micro SD #" -echo "############################################################" -echo "" -echo " This process will erase your eMMC and will install the following files" -echo " on the partitions of the eMMC." -echo "" -echo " PARTITION FILENAME" -echo " --------- --------" -echo " bootloader ${INSTALL_UBOOT_FILENAME}" -if test "${dualboot}" = "yes"; then - echo " linux_a ${INSTALL_LINUX_FILENAME}" - if test "${install_dualboot}" = "yes"; then - echo " linux_b ${INSTALL_LINUX_FILENAME}" - fi - echo " rootfs_a ${INSTALL_ROOTFS_FILENAME}" - if test "${install_dualboot}" = "yes"; then - echo " rootfs_b ${INSTALL_ROOTFS_FILENAME}" - fi -else - echo " linux ${INSTALL_LINUX_FILENAME}" - echo " recovery ${INSTALL_RECOVERY_FILENAME}" - echo " rootfs ${INSTALL_ROOTFS_FILENAME}" -fi -echo "" -echo " Press CTRL+C now if you wish to abort or wait 10 seconds" -echo " to continue." - -sleep 10 -if test $? -eq 1; then - echo "Aborted by user."; - exit; -fi - -# Skip user confirmation for U-Boot update -setenv forced_update 1 - -# Set bootdelay to zero so that firmware update is run immediately after -# the first reset. -setenv bootdelay 0 - -# Set target MMC device index to eMMC -setenv mmcdev 0 - -# Update U-Boot -echo "" -echo "" -echo ">> Installing U-Boot boot loader image ${INSTALL_UBOOT_FILENAME} (target will reset)" -echo "" -echo "" -update uboot mmc ${INSTALL_MMCDEV} ${INSTALL_UBOOT_FILENAME} -if test $? -eq 1; then - # Use old-style update with source file system argument - update uboot mmc ${INSTALL_MMCDEV} fat ${INSTALL_UBOOT_FILENAME} - if test $? -eq 1; then - echo "[ERROR] Failed to update U-Boot boot loader!"; - echo ""; - echo "Aborted."; - exit; - fi -fi - -# Set 'bootcmd' to the second part of the script that will -# - Reset environment to defaults -# - Restore 'dualboot' if previously set -# - Save the environment -# - Force on-the-fly updates to avoid possible verification errors -# - Partition the eMMC user data area for Linux -# - If Dual Boot -# - Update the system partitions: linux_a, linux_b, rootfs_a, rootfs_b -# - Set bootlimit to 3 -# - If Normal Boot: -# - Update the system partitions: linux, recovery, rootfs -# - Erase the 'update' partition -# - Configure recovery to wipe 'update' partition -# - Run 'recovery' and let the system boot after -setenv bootcmd " - env default -a; - setenv dualboot ${dualboot}; - setenv install_dualboot ${install_dualboot}; - saveenv; - setenv otf-update yes; - echo \"\"; - echo \"\"; - echo \">> Creating Linux partition table on the eMMC\"; - echo \"\"; - echo \"\"; - run partition_mmc_linux; - if test \$? -eq 1; then - echo \"[ERROR] Failed to create Linux partition table!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; - echo \"\"; - echo \"\"; - update linux_a mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux_a!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{install_dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; - echo \"\"; - echo \"\"; - update linux_b mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux_b!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; - echo \"\"; - echo \"\"; - update rootfs_a mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs_a partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{install_dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; - echo \"\"; - echo \"\"; - update rootfs_b mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs_b partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - fi; - setenv bootlimit 3; - else - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; - echo \"\"; - echo \"\"; - update linux mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; - echo \"\"; - echo \"\"; - update recovery mmc ${INSTALL_MMCDEV} ${INSTALL_RECOVERY_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update recovery partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; - echo \"\"; - echo \"\"; - update rootfs mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - setenv boot_recovery yes; - setenv recovery_command wipe_update; - fi; - setenv otf-update; - saveenv; - echo \"\"; - echo \"\"; - echo \">> Firmware installation complete.\"; - if test \"\$\{dualboot\}\" != yes; then - echo \"Rebooting into recovery mode for final deployment.\"; - fi; - echo \"\"; - echo \"\"; - sleep 1; - bootcount reset; - reset; -" - -bootcount reset -saveenv -reset diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_media.txt similarity index 83% rename from meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_usb.txt rename to meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_media.txt index 5d1ac44d6..8667432f6 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_media.txt @@ -1,5 +1,5 @@ # -# U-Boot script for installing Linux images created by Yocto from a USB stick +# U-Boot script for installing Linux images created by Yocto from external media # into the eMMC # @@ -22,8 +22,9 @@ if test "${soc_type}" = "imx93"; then fi fi fi -setenv INSTALL_UBOOT_FILENAME imx-boot-##MACHINE##${SOCREV}.bin -setenv INSTALL_USBDEV 0 + +setenv INSTALL_MEDIA_INDEX ##INSTALL_MEDIA_INDEX## +setenv INSTALL_MEDIA ##INSTALL_MEDIA## if test -z "${image-name}"; then setenv image-name ##DEFAULT_IMAGE_NAME## @@ -40,13 +41,13 @@ fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -# Check for presence of firmware files on the USB +# Check for presence of firmware files on the media FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" if test "${dualboot}" != "yes"; then FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" fi for install_f in ${FILES}; do - if test ! -e usb ${INSTALL_USBDEV} ${install_f}; then + if test ! -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${install_f}; then echo "ERROR: Could not find file ${install_f}"; install_abort=1; fi; @@ -54,9 +55,9 @@ done # Verify what kind of rootfs is going to be programmed ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" -if test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME}; then +if test -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${ROOTFS_FILENAME}; then setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; -elif test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME_SQFS}; then +elif test -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${ROOTFS_FILENAME_SQFS}; then setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; else echo "ERROR: Could not find any rootfs image"; @@ -70,7 +71,7 @@ if test "${install_abort}" = "1"; then fi echo "############################################################" -echo "# Linux firmware install from USB #" +echo "# Linux firmware install from media #" echo "############################################################" echo "" echo " This process will erase your eMMC and will install the following files" @@ -122,10 +123,10 @@ echo "" echo ">> Installing U-Boot boot loader image ${INSTALL_UBOOT_FILENAME} (target will reset)" echo "" echo "" -update uboot usb ${INSTALL_USBDEV} ${INSTALL_UBOOT_FILENAME} +update uboot ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} fat ${INSTALL_UBOOT_FILENAME} if test $? -eq 1; then # Use old-style update with source file system argument - update uboot usb ${INSTALL_USBDEV} fat ${INSTALL_UBOOT_FILENAME} + update uboot ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} fat ${INSTALL_UBOOT_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update U-Boot boot loader!"; echo ""; @@ -163,6 +164,7 @@ fi; # - Erase the 'update' partition # - Configure recovery to wipe 'update' partition # - Run 'recovery' and let the system boot after +# NOTE: insert some 'usb reset' commands to avoid EHCI timeouts setenv bootcmd " env default -a; setenv dualboot ${dualboot}; @@ -181,14 +183,16 @@ setenv bootcmd " echo \"Aborted.\"; exit; fi; - usb start; + if test \"\$\{INSTALL_MEDIA\}\" = "usb"; then + usb start; + fi; if test \"\$\{dualboot\}\" = yes; then echo \"\"; echo \"\"; echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; echo \"\"; echo \"\"; - update linux_a usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux_a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_a!\"; echo \"\"; @@ -201,7 +205,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; echo \"\"; echo \"\"; - update linux_b usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux_b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_b!\"; echo \"\"; @@ -209,12 +213,15 @@ setenv bootcmd " exit; fi; fi; + if test \"\$\{INSTALL_MEDIA\}\" = "usb"; then + usb reset; + fi; echo \"\"; echo \"\"; echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; echo \"\"; echo \"\"; - update rootfs_a usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs_a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_a partition!\"; echo \"\"; @@ -227,7 +234,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; echo \"\"; echo \"\"; - update rootfs_b usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs_b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_b partition!\"; echo \"\"; @@ -242,7 +249,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; echo \"\"; echo \"\"; - update linux usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux!\"; echo \"\"; @@ -254,7 +261,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; echo \"\"; echo \"\"; - update recovery usb ${INSTALL_USBDEV} ${INSTALL_RECOVERY_FILENAME}; + update recovery ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_RECOVERY_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update recovery partition!\"; echo \"\"; @@ -266,7 +273,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; echo \"\"; echo \"\"; - update rootfs usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs partition!\"; echo \"\"; diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_sd.txt deleted file mode 100644 index a5ba55987..000000000 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx9/install_linux_fw_sd.txt +++ /dev/null @@ -1,296 +0,0 @@ -# -# U-Boot script for installing Linux images created by Yocto from the SD -# card into the eMMC -# - -# Reset temp variables -install_abort=0 -BASEFILENAME=0 - -# Determine U-Boot file to program basing on SOM's SOC revision -if test "${soc_type}" = "imx93"; then - if test -z "${soc_rev}"; then - # Fallback to hardware version if soc_rev is empty - setexpr som_hv ${hwid_2} \& 78 - setexpr som_hv ${som_hv} / 8 - if test "${som_hv}" -lt "2"; then - SOCREV="-A0" - fi - else - if test "${soc_rev}" = "0x10"; then - SOCREV="-A0" - fi - fi -fi -setenv INSTALL_UBOOT_FILENAME imx-boot-##MACHINE##${SOCREV}.bin -setenv INSTALL_MMCDEV 1 - -if test -z "${image-name}"; then - setenv image-name ##DEFAULT_IMAGE_NAME## -fi -GRAPHICAL_IMAGES="##GRAPHICAL_IMAGES##" -for g in ${GRAPHICAL_IMAGES}; do - if test "${image-name}" = "${g}"; then - BASEFILENAME="${image-name}-##GRAPHICAL_BACKEND##" - fi -done -if test "${BASEFILENAME}" = "0"; then - BASEFILENAME="${image-name}" -fi -setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat -setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat - -# Check for presence of firmware files on the SD card -FILES="${INSTALL_UBOOT_FILENAME} ${INSTALL_LINUX_FILENAME}" -if test "${dualboot}" != "yes"; then - FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" -fi -for install_f in ${FILES}; do - if test ! -e mmc ${INSTALL_MMCDEV} ${install_f}; then - echo "ERROR: Could not find file ${install_f}"; - install_abort=1; - fi; -done -# Verify what kind of rootfs is going to be programmed -ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" -ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" -if test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME}; then - setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; -elif test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME_SQFS}; then - setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; -else - echo "ERROR: Could not find any rootfs image"; - install_abort=1; -fi -if test "${install_abort}" = "1"; then - echo "To install a different image, set variable 'image-name', e.g.:" - echo " => setenv image-name core-image-base" - echo "Aborted."; - exit; -fi - -echo "############################################################" -echo "# Linux firmware install from micro SD #" -echo "############################################################" -echo "" -echo " This process will erase your eMMC and will install the following files" -echo " on the partitions of the eMMC." -echo "" -echo " PARTITION FILENAME" -echo " --------- --------" -echo " bootloader1 ${INSTALL_UBOOT_FILENAME}" -if test "${install_redundant_bootloader}" = "yes"; then - echo " bootloader2 ${INSTALL_UBOOT_FILENAME}" -fi -if test "${dualboot}" = "yes"; then - echo " linux_a ${INSTALL_LINUX_FILENAME}" - if test "${install_dualboot}" = "yes"; then - echo " linux_b ${INSTALL_LINUX_FILENAME}" - fi - echo " rootfs_a ${INSTALL_ROOTFS_FILENAME}" - if test "${install_dualboot}" = "yes"; then - echo " rootfs_b ${INSTALL_ROOTFS_FILENAME}" - fi -else - echo " linux ${INSTALL_LINUX_FILENAME}" - echo " recovery ${INSTALL_RECOVERY_FILENAME}" - echo " rootfs ${INSTALL_ROOTFS_FILENAME}" -fi -echo "" -echo " Press CTRL+C now if you wish to abort or wait 10 seconds" -echo " to continue." - -sleep 10 -if test $? -eq 1; then - echo "Aborted by user."; - exit; -fi - -# Skip user confirmation for U-Boot update -setenv forced_update 1 - -# Set bootdelay to zero so that firmware update is run immediately after -# the first reset. -setenv bootdelay 0 - -# Set target MMC device index to eMMC -setenv mmcdev 0 - -# Update U-Boot -echo "" -echo "" -echo ">> Installing U-Boot boot loader image ${INSTALL_UBOOT_FILENAME} (target will reset)" -echo "" -echo "" -update uboot mmc ${INSTALL_MMCDEV} ${INSTALL_UBOOT_FILENAME} -if test $? -eq 1; then - # Use old-style update with source file system argument - update uboot mmc ${INSTALL_MMCDEV} fat ${INSTALL_UBOOT_FILENAME} - if test $? -eq 1; then - echo "[ERROR] Failed to update U-Boot boot loader!"; - echo ""; - echo "Aborted."; - exit; - fi -fi - -if test "${install_redundant_bootloader}" = yes; then - echo ""; - echo ""; - echo ">> Installing U-Boot boot loader image ${INSTALL_UBOOT_FILENAME} on redundant partition"; - echo ""; - echo ""; - update uboot-redundant mmc ${INSTALL_MMCDEV} ${INSTALL_UBOOT_FILENAME}; - if test $? -eq 1; then - echo "[ERROR] Failed to update redundant U-Boot boot loader!"; - echo ""; - echo "Aborted."; - exit; - fi; -fi; - -# Set 'bootcmd' to the second part of the script that will -# - Reset environment to defaults -# - Restore 'dualboot' if previously set -# - Save the environment -# - Force on-the-fly updates to avoid possible verification errors -# - Partition the eMMC user data area for Linux -# - If Dual Boot -# - Update the system partitions: linux_a, linux_b, rootfs_a, rootfs_b -# - Set bootlimit to 3 -# - If Normal Boot: -# - Update the system partitions: linux, recovery, rootfs -# - Erase the 'update' partition -# - Configure recovery to wipe 'update' partition -# - Run 'recovery' and let the system boot after -setenv bootcmd " - env default -a; - setenv dualboot ${dualboot}; - setenv install_dualboot ${install_dualboot}; - saveenv; - setenv otf-update yes; - echo \"\"; - echo \"\"; - echo \">> Creating Linux partition table on the eMMC\"; - echo \"\"; - echo \"\"; - run partition_mmc_linux; - if test \$? -eq 1; then - echo \"[ERROR] Failed to create Linux partition table!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; - echo \"\"; - echo \"\"; - update linux_a mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux_a!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{install_dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; - echo \"\"; - echo \"\"; - update linux_b mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux_b!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; - echo \"\"; - echo \"\"; - update rootfs_a mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs_a partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{install_dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; - echo \"\"; - echo \"\"; - update rootfs_b mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs_b partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - fi; - setenv bootlimit 3; - else - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; - echo \"\"; - echo \"\"; - update linux mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; - echo \"\"; - echo \"\"; - update recovery mmc ${INSTALL_MMCDEV} ${INSTALL_RECOVERY_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update recovery partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; - echo \"\"; - echo \"\"; - update rootfs mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - setenv boot_recovery yes; - setenv recovery_command wipe_update; - fi; - setenv otf-update; - saveenv; - echo \"\"; - echo \"\"; - echo \">> Firmware installation complete.\"; - if test \"\$\{dualboot\}\" != yes; then - echo \"Rebooting into recovery mode for final deployment.\"; - fi; - echo \"\"; - echo \"\"; - sleep 1; - bootcount reset; - reset; -" - -bootcount reset -saveenv -reset diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_media.txt similarity index 82% rename from meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_usb.txt rename to meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_media.txt index 3173db8f9..b22e9dbf4 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_media.txt @@ -1,5 +1,5 @@ # -# U-Boot script for installing Linux images created by Yocto from a USB stick +# U-Boot script for installing Linux images created by Yocto from external media # into the NAND # @@ -10,7 +10,9 @@ BASEFILENAME=0 setenv INSTALL_ATF_FILENAME tf-a-##MACHINE##-${module_ram}-##BOOTSCHEME_DEFAULT##-nand.stm32 setenv INSTALL_METADATA_FILENAME metadata-##MACHINE##.bin setenv INSTALL_FIP_FILENAME fip-##MACHINE##-${module_ram}-##BOOTSCHEME_DEFAULT##-nand.bin -setenv INSTALL_USBDEV 0 + +setenv INSTALL_MEDIA_INDEX ##INSTALL_MEDIA_INDEX## +setenv INSTALL_MEDIA ##INSTALL_MEDIA## if test -z "${image-name}"; then setenv image-name ##DEFAULT_IMAGE_NAME## @@ -33,7 +35,7 @@ if test "${dualboot}" != "yes"; then FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" fi for install_f in ${FILES}; do - if test ! -e usb ${INSTALL_USBDEV} ${install_f}; then + if test ! -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${install_f}; then echo "ERROR: Could not find file ${install_f}"; install_abort=1; fi; @@ -41,10 +43,10 @@ done # Verify what kind of rootfs is going to be programmed ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ubifs" ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" -if test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME}; then +if test -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${ROOTFS_FILENAME}; then setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; setenv rootfstype; -elif test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME_SQFS}; then +elif test -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${ROOTFS_FILENAME_SQFS}; then setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; setenv rootfstype squashfs; else @@ -59,7 +61,7 @@ if test "${install_abort}" = "1"; then fi echo "############################################################" -echo "# Linux firmware install from USB #" +echo "# Linux firmware install from media #" echo "############################################################" echo "" echo " This process will erase your NAND and will install the following files" @@ -110,14 +112,14 @@ echo "" echo ">> Installing ATF boot loader image ${INSTALL_ATF_FILENAME}" echo "" echo "" -update fsbl1 usb ${INSTALL_USBDEV} ${INSTALL_ATF_FILENAME} +update fsbl1 ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ATF_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update fsbl1!"; echo ""; echo "Aborted."; exit; fi -update fsbl2 usb ${INSTALL_USBDEV} ${INSTALL_ATF_FILENAME} +update fsbl2 ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ATF_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update fsbl2!"; echo ""; @@ -129,14 +131,14 @@ echo "" echo ">> Installing METADATA boot loader image ${INSTALL_METADATA_FILENAME}" echo "" echo "" -update metadata1 usb ${INSTALL_USBDEV} ${INSTALL_METADATA_FILENAME} +update metadata1 ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_METADATA_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update metadata1!"; echo ""; echo "Aborted."; exit; fi -update metadata2 usb ${INSTALL_USBDEV} ${INSTALL_METADATA_FILENAME} +update metadata2 ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_METADATA_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update metadata2!"; echo ""; @@ -148,14 +150,14 @@ echo "" echo ">> Installing FIP boot loader image ${INSTALL_FIP_FILENAME} (target will reset)" echo "" echo "" -update fip-a usb ${INSTALL_USBDEV} ${INSTALL_FIP_FILENAME} +update fip-a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_FIP_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update fip-a!"; echo ""; echo "Aborted."; exit; fi -update fip-b usb ${INSTALL_USBDEV} ${INSTALL_FIP_FILENAME} +update fip-b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_FIP_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update fip-b!"; echo ""; @@ -189,14 +191,16 @@ setenv bootcmd " setenv rootfstype ${rootfstype}; setenv install_dualboot ${install_dualboot}; run ubivolscript; - usb start; + if test \"\$\{INSTALL_MEDIA\}\" = "usb"; then + usb start; + fi; if test \"\$\{dualboot\}\" = yes; then echo \"\"; echo \"\"; echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; echo \"\"; echo \"\"; - update linux_a usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux_a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_a!\"; echo \"\"; @@ -209,7 +213,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; echo \"\"; echo \"\"; - update linux_b usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux_b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_b!\"; echo \"\"; @@ -217,12 +221,15 @@ setenv bootcmd " exit; fi; fi; + if test \"\$\{INSTALL_MEDIA\}\" = "usb"; then + usb reset; + fi; echo \"\"; echo \"\"; echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; echo \"\"; echo \"\"; - update rootfs_a usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs_a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_a partition!\"; echo \"\"; @@ -235,7 +242,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; echo \"\"; echo \"\"; - update rootfs_b usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs_b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_b partition!\"; echo \"\"; @@ -250,7 +257,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; echo \"\"; echo \"\"; - update linux usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux!\"; echo \"\"; @@ -262,7 +269,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; echo \"\"; echo \"\"; - update recovery usb ${INSTALL_USBDEV} ${INSTALL_RECOVERY_FILENAME}; + update recovery ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_RECOVERY_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update recovery partition!\"; echo \"\"; @@ -274,7 +281,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; echo \"\"; echo \"\"; - update rootfs usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs partition!\"; echo \"\"; diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_sd.txt deleted file mode 100644 index d5c42709c..000000000 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp1/install_linux_fw_sd.txt +++ /dev/null @@ -1,303 +0,0 @@ -# -# U-Boot script for installing Linux images created by Yocto from the micro SD -# card into the NAND -# - -# Reset temp variables -install_abort=0 -BASEFILENAME=0 - -setenv INSTALL_ATF_FILENAME tf-a-##MACHINE##-${module_ram}-##BOOTSCHEME_DEFAULT##-nand.stm32 -setenv INSTALL_METADATA_FILENAME metadata-##MACHINE##.bin -setenv INSTALL_FIP_FILENAME fip-##MACHINE##-${module_ram}-##BOOTSCHEME_DEFAULT##-nand.bin -setenv INSTALL_MMCDEV 1 - -if test -z "${image-name}"; then - setenv image-name ##DEFAULT_IMAGE_NAME## -fi -GRAPHICAL_IMAGES="##GRAPHICAL_IMAGES##" -for g in ${GRAPHICAL_IMAGES}; do - if test "${image-name}" = "${g}"; then - BASEFILENAME="${image-name}-##GRAPHICAL_BACKEND##" - fi -done -if test "${BASEFILENAME}" = "0"; then - BASEFILENAME="${image-name}" -fi -setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.ubifs -setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.ubifs - -# Check for presence of firmware files on the SD card -FILES="${INSTALL_ATF_FILENAME} ${INSTALL_METADATA_FILENAME} ${INSTALL_FIP_FILENAME} ${INSTALL_LINUX_FILENAME}" -if test "${dualboot}" != "yes"; then - FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" -fi -for install_f in ${FILES}; do - if test ! -e mmc ${INSTALL_MMCDEV} ${install_f}; then - echo "ERROR: Could not find file ${install_f}"; - install_abort=1; - fi; -done -# Verify what kind of rootfs is going to be programmed -ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ubifs" -ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" -if test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME}; then - setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; - setenv rootfstype; -elif test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME_SQFS}; then - setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; - setenv rootfstype squashfs; -else - echo "ERROR: Could not find any rootfs image"; - install_abort=1; -fi -if test "${install_abort}" = "1"; then - echo "To install a different image, set variable 'image-name', e.g.:" - echo " => setenv image-name core-image-base" - echo "Aborted."; - exit; -fi - -echo "############################################################" -echo "# Linux firmware install from micro SD #" -echo "############################################################" -echo "" -echo " This process will erase your NAND and will install the following files" -echo " on the partitions of the NAND." -echo "" -echo " PARTITION FILENAME" -echo " --------- --------" -echo " fsbl1 ${INSTALL_ATF_FILENAME}" -echo " fsbl2 ${INSTALL_ATF_FILENAME}" -echo " metadata1 ${INSTALL_METADATA_FILENAME}" -echo " metadata2 ${INSTALL_METADATA_FILENAME}" -echo " fip-a ${INSTALL_FIP_FILENAME}" -echo " fip-b ${INSTALL_FIP_FILENAME}" -if test "${dualboot}" = "yes"; then - echo " linux_a ${INSTALL_LINUX_FILENAME}" - if test "${install_dualboot}" = "yes"; then - echo " linux_b ${INSTALL_LINUX_FILENAME}" - fi - echo " rootfs_a ${INSTALL_ROOTFS_FILENAME}" - if test "${install_dualboot}" = "yes"; then - echo " rootfs_b ${INSTALL_ROOTFS_FILENAME}" - fi -else - echo " linux ${INSTALL_LINUX_FILENAME}" - echo " recovery ${INSTALL_RECOVERY_FILENAME}" - echo " rootfs ${INSTALL_ROOTFS_FILENAME}" -fi -echo "" -echo " Press CTRL+C now if you wish to abort or wait 10 seconds" -echo " to continue." - -sleep 10 -if test $? -eq 1; then - echo "Aborted by user."; - exit; -fi - -# Skip user confirmation for U-Boot update -setenv forced_update 1 - -# Set bootdelay to zero so that firmware update is run immediately after -# the first reset. -setenv bootdelay 0 - -# Update ATF, METADATA and FIP images -echo "" -echo "" -echo ">> Installing ATF boot loader image ${INSTALL_ATF_FILENAME}" -echo "" -echo "" -update fsbl1 mmc ${INSTALL_MMCDEV} ${INSTALL_ATF_FILENAME} -if test $? -eq 1; then - echo "[ERROR] Failed to update fsbl1!"; - echo ""; - echo "Aborted."; - exit; -fi -update fsbl2 mmc ${INSTALL_MMCDEV} ${INSTALL_ATF_FILENAME} -if test $? -eq 1; then - echo "[ERROR] Failed to update fsbl2!"; - echo ""; - echo "Aborted."; - exit; -fi -echo "" -echo "" -echo ">> Installing METADATA boot loader image ${INSTALL_METADATA_FILENAME}" -echo "" -echo "" -update metadata1 mmc ${INSTALL_MMCDEV} ${INSTALL_METADATA_FILENAME} -if test $? -eq 1; then - echo "[ERROR] Failed to update metadata1!"; - echo ""; - echo "Aborted."; - exit; -fi -update metadata2 mmc ${INSTALL_MMCDEV} ${INSTALL_METADATA_FILENAME} -if test $? -eq 1; then - echo "[ERROR] Failed to update metadata2!"; - echo ""; - echo "Aborted."; - exit; -fi -echo "" -echo "" -echo ">> Installing FIP boot loader image ${INSTALL_FIP_FILENAME} (target will reset)" -echo "" -echo "" -update fip-a mmc ${INSTALL_MMCDEV} ${INSTALL_FIP_FILENAME} -if test $? -eq 1; then - echo "[ERROR] Failed to update fip-a!"; - echo ""; - echo "Aborted."; - exit; -fi -update fip-b mmc ${INSTALL_MMCDEV} ${INSTALL_FIP_FILENAME} -if test $? -eq 1; then - echo "[ERROR] Failed to update fip-b!"; - echo ""; - echo "Aborted."; - exit; -fi - -# Check if uboot_config volume exists (U-Boot env) -ubi part UBI -ubi check uboot_config -if test $? -eq 1; then - # Create volumes layout (including U-Boot env) - run ubivolscript; -fi - -# Set 'bootcmd' to the second part of the script that will -# - Reset environment to defaults -# - Restore 'dualboot' if previously set -# - Run 'ubivolscript' script to re-partition the UBI partition and save the -# environment -# - If Dual Boot -# - Update the system partitions: linux_a, linux_b, rootfs_a, rootfs_b -# - Set bootlimit to 3 -# - If Normal Boot: -# - Update the system partitions: linux, recovery, rootfs -# - Configure recovery to wipe 'update' partition -# - Run 'recovery' and let the system boot after -setenv bootcmd " - env default -a; - setenv dualboot ${dualboot}; - setenv rootfstype ${rootfstype}; - setenv install_dualboot ${install_dualboot}; - run ubivolscript; - if test \"\$\{dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; - echo \"\"; - echo \"\"; - update linux_a mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux_a!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{install_dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; - echo \"\"; - echo \"\"; - update linux_b mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux_b!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; - echo \"\"; - echo \"\"; - update rootfs_a mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs_a partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{install_dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; - echo \"\"; - echo \"\"; - update rootfs_b mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs_b partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - fi; - setenv bootlimit 3; - else - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; - echo \"\"; - echo \"\"; - update linux mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; - echo \"\"; - echo \"\"; - update recovery mmc ${INSTALL_MMCDEV} ${INSTALL_RECOVERY_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update recovery partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; - echo \"\"; - echo \"\"; - update rootfs mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - setenv boot_recovery yes; - setenv recovery_command wipe_update; - fi; - saveenv; - echo \"\"; - echo \"\"; - echo \">> Firmware installation complete.\"; - if test \"\$\{dualboot\}\" != yes; then - echo \"Rebooting into recovery mode for final deployment.\"; - fi; - echo \"\"; - echo \"\"; - sleep 1; - bootcount reset; - reset; -" - -bootcount reset -saveenv -reset diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_usb.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_media.txt similarity index 81% rename from meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_usb.txt rename to meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_media.txt index 32235698c..b05dda55f 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_usb.txt +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_media.txt @@ -1,5 +1,5 @@ # -# U-Boot script for installing Linux images created by Yocto from a USB stick +# U-Boot script for installing Linux images created by Yocto from external media # into the eMMC # @@ -10,7 +10,8 @@ BASEFILENAME=0 setenv INSTALL_ATF_FILENAME tf-a-##MACHINE##-optee-emmc.stm32 setenv INSTALL_METADATA_FILENAME metadata-##MACHINE##.bin setenv INSTALL_FIP_FILENAME fip-##MACHINE##-optee-emmc.bin -setenv INSTALL_USBDEV 0 +setenv INSTALL_MEDIA_INDEX ##INSTALL_MEDIA_INDEX## +setenv INSTALL_MEDIA ##INSTALL_MEDIA## if test -z "${image-name}"; then setenv image-name ##DEFAULT_IMAGE_NAME## @@ -27,13 +28,13 @@ fi setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat -# Check for presence of firmware files on the USB +# Check for presence of firmware files on the media FILES="${INSTALL_ATF_FILENAME} ${INSTALL_METADATA_FILENAME} ${INSTALL_FIP_FILENAME} ${INSTALL_LINUX_FILENAME}" if test "${dualboot}" != "yes"; then FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" fi for install_f in ${FILES}; do - if test ! -e usb ${INSTALL_USBDEV} ${install_f}; then + if test ! -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${install_f}; then echo "ERROR: Could not find file ${install_f}"; install_abort=1; fi; @@ -41,9 +42,9 @@ done # Verify what kind of rootfs is going to be programmed ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" -if test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME}; then +if test -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${ROOTFS_FILENAME}; then setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; -elif test -e usb ${INSTALL_USBDEV} ${ROOTFS_FILENAME_SQFS}; then +elif test -e ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${ROOTFS_FILENAME_SQFS}; then setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; else echo "ERROR: Could not find any rootfs image"; @@ -57,7 +58,7 @@ if test "${install_abort}" = "1"; then fi echo "############################################################" -echo "# Linux firmware install from USB #" +echo "# Linux firmware install from media #" echo "############################################################" echo "" echo " This process will erase your eMMC and will install the following files" @@ -111,14 +112,14 @@ echo "" echo ">> Installing ATF boot loader image ${INSTALL_ATF_FILENAME}" echo "" echo "" -update boot1 usb ${INSTALL_USBDEV} ${INSTALL_ATF_FILENAME} +update boot1 ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ATF_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update boot1!"; echo ""; echo "Aborted."; exit; fi -update boot2 usb ${INSTALL_USBDEV} ${INSTALL_ATF_FILENAME} +update boot2 ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ATF_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update boot2!"; echo ""; @@ -130,14 +131,14 @@ echo "" echo ">> Installing METADATA boot loader image ${INSTALL_METADATA_FILENAME}" echo "" echo "" -update metadata1 usb ${INSTALL_USBDEV} ${INSTALL_METADATA_FILENAME} +update metadata1 ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_METADATA_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update metadata1!"; echo ""; echo "Aborted."; exit; fi -update metadata2 usb ${INSTALL_USBDEV} ${INSTALL_METADATA_FILENAME} +update metadata2 ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_METADATA_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update metadata2!"; echo ""; @@ -149,14 +150,14 @@ echo "" echo ">> Installing FIP boot loader image ${INSTALL_FIP_FILENAME} (target will reset)" echo "" echo "" -update fip-a usb ${INSTALL_USBDEV} ${INSTALL_FIP_FILENAME} +update fip-a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_FIP_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update fip-a!"; echo ""; echo "Aborted."; exit; fi -update fip-b usb ${INSTALL_USBDEV} ${INSTALL_FIP_FILENAME} +update fip-b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_FIP_FILENAME} if test $? -eq 1; then echo "[ERROR] Failed to update fip-b!"; echo ""; @@ -178,6 +179,7 @@ fi # - Erase the 'update' partition # - Configure recovery to wipe 'update' partition # - Run 'recovery' and let the system boot after +# NOTE: insert some 'usb reset' commands to avoid EHCI timeouts setenv bootcmd " env default -a; setenv dualboot ${dualboot}; @@ -196,14 +198,16 @@ setenv bootcmd " echo \"Aborted.\"; exit; fi; - usb start; + if test \"\$\{INSTALL_MEDIA\}\" = "usb"; then + usb start; + fi; if test \"\$\{dualboot\}\" = yes; then echo \"\"; echo \"\"; echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; echo \"\"; echo \"\"; - update linux_a usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux_a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_a!\"; echo \"\"; @@ -216,7 +220,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; echo \"\"; echo \"\"; - update linux_b usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux_b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux_b!\"; echo \"\"; @@ -224,12 +228,15 @@ setenv bootcmd " exit; fi; fi; + if test \"\$\{INSTALL_MEDIA\}\" = "usb"; then + usb reset; + fi; echo \"\"; echo \"\"; echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; echo \"\"; echo \"\"; - update rootfs_a usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs_a ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_a partition!\"; echo \"\"; @@ -242,7 +249,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; echo \"\"; echo \"\"; - update rootfs_b usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs_b ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs_b partition!\"; echo \"\"; @@ -257,7 +264,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; echo \"\"; echo \"\"; - update linux usb ${INSTALL_USBDEV} ${INSTALL_LINUX_FILENAME}; + update linux ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_LINUX_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update linux!\"; echo \"\"; @@ -269,7 +276,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; echo \"\"; echo \"\"; - update recovery usb ${INSTALL_USBDEV} ${INSTALL_RECOVERY_FILENAME}; + update recovery ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_RECOVERY_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update recovery partition!\"; echo \"\"; @@ -281,7 +288,7 @@ setenv bootcmd " echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; echo \"\"; echo \"\"; - update rootfs usb ${INSTALL_USBDEV} ${INSTALL_ROOTFS_FILENAME}; + update rootfs ${INSTALL_MEDIA} ${INSTALL_MEDIA_INDEX} ${INSTALL_ROOTFS_FILENAME}; if test \$? -eq 1; then echo \"[ERROR] Failed to update rootfs partition!\"; echo \"\"; diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_sd.txt b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_sd.txt deleted file mode 100644 index bc3c38c8a..000000000 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccmp2/install_linux_fw_sd.txt +++ /dev/null @@ -1,311 +0,0 @@ -# -# U-Boot script for installing Linux images created by Yocto from the micro SD -# card into the eMMC -# - -# Reset temp variables -install_abort=0 -BASEFILENAME=0 - -setenv INSTALL_ATF_FILENAME tf-a-##MACHINE##-optee-emmc.stm32 -setenv INSTALL_METADATA_FILENAME metadata-##MACHINE##.bin -setenv INSTALL_FIP_FILENAME fip-##MACHINE##-optee-emmc.bin -setenv INSTALL_MMCDEV 2 - -if test -z "${image-name}"; then - setenv image-name ##DEFAULT_IMAGE_NAME## -fi -GRAPHICAL_IMAGES="##GRAPHICAL_IMAGES##" -for g in ${GRAPHICAL_IMAGES}; do - if test "${image-name}" = "${g}"; then - BASEFILENAME="${image-name}-##GRAPHICAL_BACKEND##" - fi -done -if test "${BASEFILENAME}" = "0"; then - BASEFILENAME="${image-name}" -fi -setenv INSTALL_LINUX_FILENAME ${BASEFILENAME}-##MACHINE##.boot.vfat -setenv INSTALL_RECOVERY_FILENAME ${BASEFILENAME}-##MACHINE##.recovery.vfat - -# Check for presence of firmware files on the SD card -FILES="${INSTALL_ATF_FILENAME} ${INSTALL_METADATA_FILENAME} ${INSTALL_FIP_FILENAME} ${INSTALL_LINUX_FILENAME}" -if test "${dualboot}" != "yes"; then - FILES="${FILES} ${INSTALL_RECOVERY_FILENAME}" -fi -for install_f in ${FILES}; do - if test ! -e mmc ${INSTALL_MMCDEV} ${install_f}; then - echo "ERROR: Could not find file ${install_f}"; - install_abort=1; - fi; -done -# Verify what kind of rootfs is going to be programmed -ROOTFS_FILENAME="${BASEFILENAME}-##MACHINE##.ext4" -ROOTFS_FILENAME_SQFS="${BASEFILENAME}-##MACHINE##.squashfs" -if test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME}; then - setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME}; -elif test -e mmc ${INSTALL_MMCDEV} ${ROOTFS_FILENAME_SQFS}; then - setenv INSTALL_ROOTFS_FILENAME ${ROOTFS_FILENAME_SQFS}; -else - echo "ERROR: Could not find any rootfs image"; - install_abort=1; -fi -if test "${install_abort}" = "1"; then - echo "To install a different image, set variable 'image-name', e.g.:" - echo " => setenv image-name core-image-base" - echo "Aborted."; - exit; -fi - -echo "############################################################" -echo "# Linux firmware install from micro SD #" -echo "############################################################" -echo "" -echo " This process will erase your eMMC and will install the following files" -echo " on the partitions of the eMMC." -echo "" -echo " PARTITION FILENAME" -echo " --------- --------" -echo " boot1 ${INSTALL_ATF_FILENAME}" -echo " boot2 ${INSTALL_ATF_FILENAME}" -echo " metadata1 ${INSTALL_METADATA_FILENAME}" -echo " metadata2 ${INSTALL_METADATA_FILENAME}" -echo " fip-a ${INSTALL_FIP_FILENAME}" -echo " fip-b ${INSTALL_FIP_FILENAME}" -if test "${dualboot}" = "yes"; then - echo " linux_a ${INSTALL_LINUX_FILENAME}" - if test "${install_dualboot}" = "yes"; then - echo " linux_b ${INSTALL_LINUX_FILENAME}" - fi - echo " rootfs_a ${INSTALL_ROOTFS_FILENAME}" - if test "${install_dualboot}" = "yes"; then - echo " rootfs_b ${INSTALL_ROOTFS_FILENAME}" - fi -else - echo " linux ${INSTALL_LINUX_FILENAME}" - echo " recovery ${INSTALL_RECOVERY_FILENAME}" - echo " rootfs ${INSTALL_ROOTFS_FILENAME}" -fi -echo "" -echo " Press CTRL+C now if you wish to abort or wait 10 seconds" -echo " to continue." - -sleep 10 -if test $? -eq 1; then - echo "Aborted by user."; - exit; -fi - -# Skip user confirmation for U-Boot update -setenv forced_update 1 - -# Set bootdelay to zero so that firmware update is run immediately after -# the first reset. -setenv bootdelay 0 - -# Set target MMC device index to eMMC -setenv mmcdev 0 - -# Update ATF, METADATA and FIP images -echo "" -echo "" -echo ">> Installing ATF boot loader image ${INSTALL_ATF_FILENAME}" -echo "" -echo "" -update boot1 mmc ${INSTALL_MMCDEV} ${INSTALL_ATF_FILENAME} -if test $? -eq 1; then - echo "[ERROR] Failed to update boot1!"; - echo ""; - echo "Aborted."; - exit; -fi -update boot2 mmc ${INSTALL_MMCDEV} ${INSTALL_ATF_FILENAME} -if test $? -eq 1; then - echo "[ERROR] Failed to update boot2!"; - echo ""; - echo "Aborted."; - exit; -fi -echo "" -echo "" -echo ">> Installing METADATA boot loader image ${INSTALL_METADATA_FILENAME}" -echo "" -echo "" -update metadata1 mmc ${INSTALL_MMCDEV} ${INSTALL_METADATA_FILENAME} -if test $? -eq 1; then - echo "[ERROR] Failed to update metadata1!"; - echo ""; - echo "Aborted."; - exit; -fi -update metadata2 mmc ${INSTALL_MMCDEV} ${INSTALL_METADATA_FILENAME} -if test $? -eq 1; then - echo "[ERROR] Failed to update metadata2!"; - echo ""; - echo "Aborted."; - exit; -fi -echo "" -echo "" -echo ">> Installing FIP boot loader image ${INSTALL_FIP_FILENAME} (target will reset)" -echo "" -echo "" -update fip-a mmc ${INSTALL_MMCDEV} ${INSTALL_FIP_FILENAME} -if test $? -eq 1; then - echo "[ERROR] Failed to update fip-a!"; - echo ""; - echo "Aborted."; - exit; -fi -update fip-b mmc ${INSTALL_MMCDEV} ${INSTALL_FIP_FILENAME} -if test $? -eq 1; then - echo "[ERROR] Failed to update fip-b!"; - echo ""; - echo "Aborted."; - exit; -fi - -# Set 'bootcmd' to the second part of the script that will -# - Reset environment to defaults -# - Restore 'dualboot' if previously set -# - Save the environment -# - Force on-the-fly updates to avoid possible verification errors -# - Partition the eMMC user data area for Linux -# - If Dual Boot -# - Update the system partitions: linux_a, linux_b, rootfs_a, rootfs_b -# - Set bootlimit to 3 -# - If Normal Boot: -# - Update the system partitions: linux, recovery, rootfs -# - Erase the 'update' partition -# - Configure recovery to wipe 'update' partition -# - Run 'recovery' and let the system boot after -setenv bootcmd " - env default -a; - setenv dualboot ${dualboot}; - setenv install_dualboot ${install_dualboot}; - saveenv; - setenv otf-update yes; - echo \"\"; - echo \"\"; - echo \">> Creating Linux partition table on the eMMC\"; - echo \"\"; - echo \"\"; - run partition_mmc_linux; - if test \$? -eq 1; then - echo \"[ERROR] Failed to create Linux partition table!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_a\"; - echo \"\"; - echo \"\"; - update linux_a mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux_a!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{install_dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux_b\"; - echo \"\"; - echo \"\"; - update linux_b mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux_b!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_a\"; - echo \"\"; - echo \"\"; - update rootfs_a mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs_a partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - if test \"\$\{install_dualboot\}\" = yes; then - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs_b\"; - echo \"\"; - echo \"\"; - update rootfs_b mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs_b partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - fi; - setenv bootlimit 3; - else - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_LINUX_FILENAME} on linux\"; - echo \"\"; - echo \"\"; - update linux mmc ${INSTALL_MMCDEV} ${INSTALL_LINUX_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update linux!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_RECOVERY_FILENAME} on recovery\"; - echo \"\"; - echo \"\"; - update recovery mmc ${INSTALL_MMCDEV} ${INSTALL_RECOVERY_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update recovery partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - echo \"\"; - echo \">> Installing file ${INSTALL_ROOTFS_FILENAME} on rootfs\"; - echo \"\"; - echo \"\"; - update rootfs mmc ${INSTALL_MMCDEV} ${INSTALL_ROOTFS_FILENAME}; - if test \$? -eq 1; then - echo \"[ERROR] Failed to update rootfs partition!\"; - echo \"\"; - echo \"Aborted.\"; - exit; - fi; - echo \"\"; - setenv boot_recovery yes; - setenv recovery_command wipe_update; - fi; - setenv otf-update; - saveenv; - echo \"\"; - echo \"\"; - echo \">> Firmware installation complete.\"; - if test \"\$\{dualboot\}\" != yes; then - echo \"Rebooting into recovery mode for final deployment.\"; - fi; - echo \"\"; - echo \"\"; - sleep 1; - bootcount reset; - reset; -" - -bootcount reset -saveenv -reset From a20aadbeec692cfaffe9a71a2068cd5450bc90cf Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Fri, 6 Jun 2025 13:26:03 +0200 Subject: [PATCH 117/138] bluez5-init: add Bluetooth init script for CCMP1x platforms The HCI_UART Bluetooth driver does not support suspend-to-RAM operation, so the driver must be loaded and unloaded manually. This commit adds support for the Bluetooth initialization script used across Digi platforms, specifically for ConnectCore MP13 and MP15. https://onedigi.atlassian.net/browse/DEL-9650 Signed-off-by: Arturo Buzarra --- meta-digi-arm/conf/machine/ccmp13-dvk.conf | 3 - meta-digi-arm/conf/machine/ccmp15-dvk.conf | 3 - .../recipes-kernel/linux/linux-dey_6.6.bb | 4 + .../recipes-connectivity/bluez/bluez5-init.bb | 8 +- .../bluez/bluez5-init/ccmp1/bluetooth-init | 93 +++++++++++++++++++ .../{stm32mpcommon => ccmp2}/bluetooth-init | 0 6 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 meta-digi-dey/recipes-connectivity/bluez/bluez5-init/ccmp1/bluetooth-init rename meta-digi-dey/recipes-connectivity/bluez/bluez5-init/{stm32mpcommon => ccmp2}/bluetooth-init (100%) diff --git a/meta-digi-arm/conf/machine/ccmp13-dvk.conf b/meta-digi-arm/conf/machine/ccmp13-dvk.conf index 9302b5215..0fad3d8de 100644 --- a/meta-digi-arm/conf/machine/ccmp13-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp13-dvk.conf @@ -146,9 +146,6 @@ HAS_WIFI_VIRTWLANS = "true" # Set this platform without graphical support IS_HEADLESS = "true" -# Remove additional bluetooth packages -MACHINE_BLUETOOTH_EXTRA_INSTALL = "" - # XBee XBEE_RESET_N_GPIO ?= "GPIOG@15" XBEE_TTY ?= "ttySTM2" diff --git a/meta-digi-arm/conf/machine/ccmp15-dvk.conf b/meta-digi-arm/conf/machine/ccmp15-dvk.conf index 0462335ac..1f30afb6a 100644 --- a/meta-digi-arm/conf/machine/ccmp15-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp15-dvk.conf @@ -159,9 +159,6 @@ MKUBIFS_ARGS ?= "-m 2048 -e 126976 -c 8191" # Wireless external module HAS_WIFI_VIRTWLANS = "true" -# Remove additional bluetooth packages -MACHINE_BLUETOOTH_EXTRA_INSTALL = "" - # XBee XBEE_RESET_N_GPIO ?= "GPIOZ@2" XBEE_TTY ?= "ttySTM2" 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 99764725b..835092307 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 @@ -46,6 +46,10 @@ do_compile:append:stm32mpcommon() { fi } +# Blacklist hci_uart module. It will be managed by the bluetooth-init script +KERNEL_MODULE_PROBECONF:ccmp1 += "hci_uart" +module_conf_hci_uart:ccmp1 = "blacklist hci_uart" + do_install:append:stm32mpcommon() { if ${@bb.utils.contains('MACHINE_FEATURES','gpu','true','false',d)}; then # when ACCEPT_EULA are filled diff --git a/meta-digi-dey/recipes-connectivity/bluez/bluez5-init.bb b/meta-digi-dey/recipes-connectivity/bluez/bluez5-init.bb index fe596bbab..f6633b090 100644 --- a/meta-digi-dey/recipes-connectivity/bluez/bluez5-init.bb +++ b/meta-digi-dey/recipes-connectivity/bluez/bluez5-init.bb @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2024, Digi International Inc. +# Copyright (C) 2022-2025, Digi International Inc. SUMMARY = "Bluetooth init scripts" LICENSE = "GPL-2.0-only" @@ -60,7 +60,9 @@ RDEPENDS:${PN} = "initscripts-functions" # IW61x Bluetooth support requires the WiFi FW support RDEPENDS:${PN}:append:ccimx9 = " firmware-nxp-wifi-nxpiw612-sdio" -RDEPENDS:${PN}:append:ccmp25 = " firmware-murata-infineon" +# Murata - Infineon combo chip requires both WiFi and Bluetooth firmware +RDEPENDS:${PN}:append:ccmp1 = " firmware-murata-infineon" +RDEPENDS:${PN}:append:ccmp2 = " firmware-murata-infineon" PACKAGE_ARCH = "${MACHINE_ARCH}" -COMPATIBLE_MACHINE = "(ccimx6$|ccimx6ul|ccimx8m|ccimx8x|ccimx9|ccmp25)" +COMPATIBLE_MACHINE = "(ccimx6$|ccimx6ul|ccimx8m|ccimx8x|ccimx9|ccmp1|ccmp2)" diff --git a/meta-digi-dey/recipes-connectivity/bluez/bluez5-init/ccmp1/bluetooth-init b/meta-digi-dey/recipes-connectivity/bluez/bluez5-init/ccmp1/bluetooth-init new file mode 100644 index 000000000..1971a47c4 --- /dev/null +++ b/meta-digi-dey/recipes-connectivity/bluez/bluez5-init/ccmp1/bluetooth-init @@ -0,0 +1,93 @@ +#!/bin/sh +#=============================================================================== +# +# Copyright (C) 2025 by Digi International Inc. +# All rights reserved. +# +# 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. +# +# +# !Description: Initialize Bluetooth interface +# +#=============================================================================== + +SCRIPTNAME="$(basename ${0})" +RETRIES=3 +HCI_IFACE="hci0" +MODULE_NAME="hci_uart" + +if grep -qs '\' /proc/device-tree/compatible; then + # CCMP13 - BT_REG_EN - GPIO PD13 + GPIO_CHIP="GPIOD" + GPIO_NUMBER="13" +else + # CCMP15 - BT_REG_EN - GPIO PZ6 + GPIO_CHIP="GPIOZ" + GPIO_NUMBER="6" +fi + +log() { + if type "systemd-cat" >/dev/null 2>/dev/null; then + systemd-cat -p "${1}" -t "${SCRIPTNAME}" printf "%s" "${2}" + else + logger -p "${1}" -t "${SCRIPTNAME}" "${2}" + fi +} + +bt_power() { + gpioset "${GPIO_CHIP}" "${GPIO_NUMBER}"="${1}" +} + +is_kernel_module_loaded() { + lsmod | grep -qs -w "^${MODULE_NAME}" +} + +bt_init() { + # If module is already loaded, skip + is_kernel_module_loaded && log info "kernel module already present, skipping" && return 1 + + for i in $(seq ${RETRIES}); do + # Perform a power cycle + bt_power 0 + sleep 0.5 + bt_power 1 + + # Load manually the kernel module + modprobe "${MODULE_NAME}" + sleep 1 + is_kernel_module_loaded && log info "Bluetooth activated" && return 0 + + log err "Cannot initialize Bluetooth, retrying..." + done + + log err "Failed to initialize Bluetooth interface" + return 1 +} + +bt_stop() { + hciconfig ${HCI_IFACE} down + # Unload HCI driver instance + rmmod hci_uart + rmmod btbcm + # Disable Bluetooth chip + bt_power 0 && log info "Bluetooth disabled" && return 0 +} + +case "$1" in + start) + bt_init + ;; + stop) + bt_stop + ;; + restart) + $0 stop + $0 start + ;; + *) + echo "Usage: $0 {start|stop|restart}" + exit 1 + ;; +esac diff --git a/meta-digi-dey/recipes-connectivity/bluez/bluez5-init/stm32mpcommon/bluetooth-init b/meta-digi-dey/recipes-connectivity/bluez/bluez5-init/ccmp2/bluetooth-init similarity index 100% rename from meta-digi-dey/recipes-connectivity/bluez/bluez5-init/stm32mpcommon/bluetooth-init rename to meta-digi-dey/recipes-connectivity/bluez/bluez5-init/ccmp2/bluetooth-init From fc641ce6807e9b994e0dd6421c4b5725ffe4fea0 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Fri, 13 Jun 2025 10:38:24 +0200 Subject: [PATCH 118/138] dey-image: enable DEY_IMAGE_INSTALLER by default This generates an installer.zip image with all the firmware images, including install scripts for UUU, SD card, and USB. This is helpful to share the artifacts with manufacturing or for deploying to external media such as microSD or USB stick. Signed-off-by: Hector Palacios --- meta-digi-dey/classes/dey-image.bbclass | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meta-digi-dey/classes/dey-image.bbclass b/meta-digi-dey/classes/dey-image.bbclass index 1b44ddb98..ec653c9d9 100644 --- a/meta-digi-dey/classes/dey-image.bbclass +++ b/meta-digi-dey/classes/dey-image.bbclass @@ -24,9 +24,9 @@ EXTRA_USERS_PARAMS += "\ " # -# Generate ZIP installer if configured in the project's local.conf +# Generate ZIP installer unless disabled in the project's local.conf # -DEY_IMAGE_INSTALLER ?= "0" +DEY_IMAGE_INSTALLER ?= "1" inherit ${@oe.utils.conditional("DEY_IMAGE_INSTALLER", "1", "dey-image-installer", "", d)} # From 57f0575ad067bbba3ab0830b3c623f5fd7ed0fea Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Fri, 13 Jun 2025 10:44:30 +0200 Subject: [PATCH 119/138] machine: remove 'sdcard.gz' image from default IMAGE_FSTYPES The generation of the sdcard image takes time and resources, and it's not involved in the Get Started. This can be easily re-enabled by appending the variable in the project local.conf. Append the variable in the build scripts, to facilitate its usage on release builds. Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/ccimx6qpsbc.conf | 2 +- meta-digi-arm/conf/machine/ccimx6sbc.conf | 2 +- meta-digi-arm/conf/machine/ccimx8mm-dvk.conf | 2 +- meta-digi-arm/conf/machine/ccimx8mn-dvk.conf | 2 +- meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf | 2 +- meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf | 2 +- meta-digi-arm/conf/machine/include/ccimx9.inc | 2 +- sdk/build-github.sh | 5 +++++ sdk/build.sh | 5 +++++ 9 files changed, 17 insertions(+), 7 deletions(-) diff --git a/meta-digi-arm/conf/machine/ccimx6qpsbc.conf b/meta-digi-arm/conf/machine/ccimx6qpsbc.conf index 5f9488515..f843091da 100644 --- a/meta-digi-arm/conf/machine/ccimx6qpsbc.conf +++ b/meta-digi-arm/conf/machine/ccimx6qpsbc.conf @@ -50,7 +50,7 @@ XBEE_TTY ?= "ttymxc4" # Image FS types IMAGE_FSTYPES ?= '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ "boot.vfat tar.xz recovery.vfat squashfs", \ - "boot.vfat ext4.gz sdcard.gz tar.xz recovery.vfat", d)}' + "boot.vfat ext4.gz tar.xz recovery.vfat", d)}' # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = " \ diff --git a/meta-digi-arm/conf/machine/ccimx6sbc.conf b/meta-digi-arm/conf/machine/ccimx6sbc.conf index 0f38026c2..0eca09ba6 100644 --- a/meta-digi-arm/conf/machine/ccimx6sbc.conf +++ b/meta-digi-arm/conf/machine/ccimx6sbc.conf @@ -52,7 +52,7 @@ XBEE_TTY ?= "ttymxc4" # Image FS types IMAGE_FSTYPES ?= '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ "boot.vfat tar.xz recovery.vfat squashfs", \ - "boot.vfat ext4.gz sdcard.gz tar.xz recovery.vfat", d)}' + "boot.vfat ext4.gz tar.xz recovery.vfat", d)}' # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = " \ diff --git a/meta-digi-arm/conf/machine/ccimx8mm-dvk.conf b/meta-digi-arm/conf/machine/ccimx8mm-dvk.conf index 5795adf16..1de9b6bce 100644 --- a/meta-digi-arm/conf/machine/ccimx8mm-dvk.conf +++ b/meta-digi-arm/conf/machine/ccimx8mm-dvk.conf @@ -57,7 +57,7 @@ XBEE_TTY ?= "ttymxc3" # Image FS types IMAGE_FSTYPES ?= '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ "boot.vfat tar.xz recovery.vfat squashfs", \ - "boot.vfat ext4.gz sdcard.gz tar.xz recovery.vfat", d)}' + "boot.vfat ext4.gz tar.xz recovery.vfat", d)}' # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = "imx-boot-ccimx8mm-dvk.bin" diff --git a/meta-digi-arm/conf/machine/ccimx8mn-dvk.conf b/meta-digi-arm/conf/machine/ccimx8mn-dvk.conf index b546c00bc..7b2a6788e 100644 --- a/meta-digi-arm/conf/machine/ccimx8mn-dvk.conf +++ b/meta-digi-arm/conf/machine/ccimx8mn-dvk.conf @@ -58,7 +58,7 @@ XBEE_TTY ?= "ttymxc3" # Image FS types IMAGE_FSTYPES ?= '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ "boot.vfat tar.xz recovery.vfat squashfs", \ - "boot.vfat ext4.gz sdcard.gz tar.xz recovery.vfat", d)}' + "boot.vfat ext4.gz tar.xz recovery.vfat", d)}' # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = "imx-boot-ccimx8mn-dvk.bin" diff --git a/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf b/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf index 1644cb6c7..9040b7bdb 100644 --- a/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf +++ b/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf @@ -35,7 +35,7 @@ XBEE_TTY ?= "ttyLP0" # Image FS types IMAGE_FSTYPES ?= '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ "boot.vfat tar.xz recovery.vfat squashfs", \ - "boot.vfat ext4.gz sdcard.gz tar.xz recovery.vfat", d)}' + "boot.vfat ext4.gz tar.xz recovery.vfat", d)}' # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = " \ diff --git a/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf b/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf index 073faefe4..c7b38fc5a 100644 --- a/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf +++ b/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf @@ -50,7 +50,7 @@ XBEE_TTY ?= "ttyMCA0" # Image FS types IMAGE_FSTYPES ?= '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ "boot.vfat tar.xz recovery.vfat squashfs", \ - "boot.vfat ext4.gz sdcard.gz tar.xz recovery.vfat", d)}' + "boot.vfat ext4.gz tar.xz recovery.vfat", d)}' # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = " \ diff --git a/meta-digi-arm/conf/machine/include/ccimx9.inc b/meta-digi-arm/conf/machine/include/ccimx9.inc index c6bc929d5..b2ed62b54 100644 --- a/meta-digi-arm/conf/machine/include/ccimx9.inc +++ b/meta-digi-arm/conf/machine/include/ccimx9.inc @@ -100,7 +100,7 @@ XBEE_TTY ?= "ttyLP3" # Image FS types IMAGE_FSTYPES ?= '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ "boot.vfat recovery.vfat squashfs tar.xz", \ - "boot.vfat recovery.vfat ext4.gz tar.xz sdcard.gz", d)}' + "boot.vfat recovery.vfat ext4.gz tar.xz", d)}' # Default overlayfs_etc mount point and type OVERLAYFS_ETC_MOUNT_POINT ?= "/mnt/data" diff --git a/sdk/build-github.sh b/sdk/build-github.sh index 4e42df27c..f2a3ef172 100755 --- a/sdk/build-github.sh +++ b/sdk/build-github.sh @@ -36,6 +36,10 @@ ZIP_INSTALLER_CFG=" DEY_IMAGE_INSTALLER = \"1\" " +SDCARD_FSTYPE=" +IMAGE_FSTYPES:append = \" sdcard.gz\" +" + SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(date +%s)}" BUILD_TIMESTAMP=" SOURCE_DATE_EPOCH = \"${SOURCE_DATE_EPOCH}\" @@ -237,6 +241,7 @@ for platform in ${DY_PLATFORMS}; do { printf "%s" "${RM_WORK_CFG}" printf "%s" "${ZIP_INSTALLER_CFG}" + printf "%s" "${SDCARD_FSTYPE}" printf "%s" "${BUILD_TIMESTAMP}" } >> conf/local.conf for target in ${platform_targets:?}; do diff --git a/sdk/build.sh b/sdk/build.sh index 57626cdd4..fb83dbbfa 100755 --- a/sdk/build.sh +++ b/sdk/build.sh @@ -58,6 +58,10 @@ ZIP_INSTALLER_CFG=" DEY_IMAGE_INSTALLER = \"1\" " +SDCARD_FSTYPE=" +IMAGE_FSTYPES:append = \" sdcard.gz\" +" + SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(date +%s)}" BUILD_TIMESTAMP=" SOURCE_DATE_EPOCH = \"${SOURCE_DATE_EPOCH}\" @@ -275,6 +279,7 @@ for platform in ${DY_PLATFORMS}; do printf "%s" "${RM_WORK_CFG}" >> conf/local.conf fi printf "%s" "${ZIP_INSTALLER_CFG}" >> conf/local.conf + printf "%s" "${SDCARD_FSTYPE}" >> conf/local.conf # Append extra configuration macros if provided from build environment if [ -n "${DY_EXTRA_LOCAL_CONF}" ]; then printf "%s\n" "${DY_EXTRA_LOCAL_CONF}" >> conf/local.conf From 0aed6a9c7369584016272145232c8ea0a9f25a41 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Fri, 13 Jun 2025 12:37:02 +0200 Subject: [PATCH 120/138] move IMAGE_FSTYPES to digi-defaults.inc IMAGE_FSTYPES was declared on each platform config file, but it has the same values for all platforms depending on the storage media (mmc or mtd) and whether read-only is enabled. Move the conditional weak assignment to digi-default.inc and remove it from each platform config. In the case of STM platforms, since IMAGE_FSTYPES is weak-assigned by STM layer, we still need to append/remove from it inside the platform config, but move it to the family includes, rather than declaring it on each specific platform. Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/ccimx6qpsbc.conf | 5 ----- meta-digi-arm/conf/machine/ccimx6sbc.conf | 5 ----- meta-digi-arm/conf/machine/ccimx6ulsbc.conf | 5 ----- meta-digi-arm/conf/machine/ccimx6ulstarter.conf | 5 ----- meta-digi-arm/conf/machine/ccimx8mm-dvk.conf | 5 ----- meta-digi-arm/conf/machine/ccimx8mn-dvk.conf | 5 ----- .../conf/machine/ccimx8x-sbc-express.conf | 5 ----- meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf | 5 ----- meta-digi-arm/conf/machine/ccmp13-dvk.conf | 10 ---------- meta-digi-arm/conf/machine/ccmp15-dvk.conf | 10 ---------- meta-digi-arm/conf/machine/ccmp25-dvk.conf | 10 ---------- meta-digi-arm/conf/machine/include/ccimx9.inc | 5 ----- meta-digi-arm/conf/machine/include/ccmp1.inc | 6 +++++- meta-digi-arm/conf/machine/include/ccmp2.inc | 6 +++++- .../conf/machine/include/digi-defaults.inc | 14 ++++++++++++++ 15 files changed, 24 insertions(+), 77 deletions(-) diff --git a/meta-digi-arm/conf/machine/ccimx6qpsbc.conf b/meta-digi-arm/conf/machine/ccimx6qpsbc.conf index f843091da..73be213f8 100644 --- a/meta-digi-arm/conf/machine/ccimx6qpsbc.conf +++ b/meta-digi-arm/conf/machine/ccimx6qpsbc.conf @@ -47,11 +47,6 @@ BT_TTY ?= "ttymxc1" # XBee XBEE_TTY ?= "ttymxc4" -# Image FS types -IMAGE_FSTYPES ?= '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ - "boot.vfat tar.xz recovery.vfat squashfs", \ - "boot.vfat ext4.gz tar.xz recovery.vfat", d)}' - # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = " \ u-boot-ccimx6qpsbc1GB.imx \ diff --git a/meta-digi-arm/conf/machine/ccimx6sbc.conf b/meta-digi-arm/conf/machine/ccimx6sbc.conf index 0eca09ba6..232a9c350 100644 --- a/meta-digi-arm/conf/machine/ccimx6sbc.conf +++ b/meta-digi-arm/conf/machine/ccimx6sbc.conf @@ -49,11 +49,6 @@ BT_TTY ?= "ttymxc1" # XBee XBEE_TTY ?= "ttymxc4" -# Image FS types -IMAGE_FSTYPES ?= '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ - "boot.vfat tar.xz recovery.vfat squashfs", \ - "boot.vfat ext4.gz tar.xz recovery.vfat", d)}' - # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = " \ u-boot-ccimx6dlsbc512MB.imx \ diff --git a/meta-digi-arm/conf/machine/ccimx6ulsbc.conf b/meta-digi-arm/conf/machine/ccimx6ulsbc.conf index f9667d9fb..5daf216bf 100644 --- a/meta-digi-arm/conf/machine/ccimx6ulsbc.conf +++ b/meta-digi-arm/conf/machine/ccimx6ulsbc.conf @@ -34,11 +34,6 @@ BT_TTY ?= "ttymxc0" # XBee XBEE_TTY ?= "ttymxc1" -# Image FS types -IMAGE_FSTYPES ?= '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ - "tar.xz boot.ubifs recovery.ubifs squashfs", \ - "tar.xz ubifs boot.ubifs recovery.ubifs", d)}' - # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = " \ u-boot-ccimx6ulsbc1GB.imx \ diff --git a/meta-digi-arm/conf/machine/ccimx6ulstarter.conf b/meta-digi-arm/conf/machine/ccimx6ulstarter.conf index 2d653a4a6..6771aafdd 100644 --- a/meta-digi-arm/conf/machine/ccimx6ulstarter.conf +++ b/meta-digi-arm/conf/machine/ccimx6ulstarter.conf @@ -30,11 +30,6 @@ SERIAL_CONSOLES ?= "115200;ttymxc4" # Bluetooth tty BT_TTY ?= "ttymxc0" -# Image FS types -IMAGE_FSTYPES ?= '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ - "tar.xz boot.ubifs recovery.ubifs squashfs", \ - "tar.xz ubifs boot.ubifs recovery.ubifs", d)}' - # Default image for install scripts DEFAULT_IMAGE_NAME ?= "core-image-base" diff --git a/meta-digi-arm/conf/machine/ccimx8mm-dvk.conf b/meta-digi-arm/conf/machine/ccimx8mm-dvk.conf index 1de9b6bce..4f296ec50 100644 --- a/meta-digi-arm/conf/machine/ccimx8mm-dvk.conf +++ b/meta-digi-arm/conf/machine/ccimx8mm-dvk.conf @@ -54,11 +54,6 @@ XBEE_RESET_N_GPIO ?= "mca-gpio@15,gpio1@8" XBEE_SLEEP_RQ_GPIO ?= "mca-gpio@11,gpio1@7" XBEE_TTY ?= "ttymxc3" -# Image FS types -IMAGE_FSTYPES ?= '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ - "boot.vfat tar.xz recovery.vfat squashfs", \ - "boot.vfat ext4.gz tar.xz recovery.vfat", d)}' - # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = "imx-boot-ccimx8mm-dvk.bin" diff --git a/meta-digi-arm/conf/machine/ccimx8mn-dvk.conf b/meta-digi-arm/conf/machine/ccimx8mn-dvk.conf index 7b2a6788e..3270b8096 100644 --- a/meta-digi-arm/conf/machine/ccimx8mn-dvk.conf +++ b/meta-digi-arm/conf/machine/ccimx8mn-dvk.conf @@ -55,11 +55,6 @@ XBEE_RESET_N_GPIO ?= "mca-gpio@15,gpio1@8" XBEE_SLEEP_RQ_GPIO ?= "mca-gpio@11,gpio1@7" XBEE_TTY ?= "ttymxc3" -# Image FS types -IMAGE_FSTYPES ?= '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ - "boot.vfat tar.xz recovery.vfat squashfs", \ - "boot.vfat ext4.gz tar.xz recovery.vfat", d)}' - # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = "imx-boot-ccimx8mn-dvk.bin" diff --git a/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf b/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf index 9040b7bdb..ffd76bad3 100644 --- a/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf +++ b/meta-digi-arm/conf/machine/ccimx8x-sbc-express.conf @@ -32,11 +32,6 @@ XBEE_RESET_N_GPIO ?= "gpio3@13" XBEE_SLEEP_RQ_GPIO ?= "gpio3@16" XBEE_TTY ?= "ttyLP0" -# Image FS types -IMAGE_FSTYPES ?= '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ - "boot.vfat tar.xz recovery.vfat squashfs", \ - "boot.vfat ext4.gz tar.xz recovery.vfat", d)}' - # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = " \ imx-boot-ccimx8x-sbc-express-B0.bin \ diff --git a/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf b/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf index c7b38fc5a..fd55f8cb2 100644 --- a/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf +++ b/meta-digi-arm/conf/machine/ccimx8x-sbc-pro.conf @@ -47,11 +47,6 @@ XBEE_RESET_N_GPIO ?= "mca-gpio@15" XBEE_SLEEP_RQ_GPIO ?= "mca-gpio@11" XBEE_TTY ?= "ttyMCA0" -# Image FS types -IMAGE_FSTYPES ?= '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ - "boot.vfat tar.xz recovery.vfat squashfs", \ - "boot.vfat ext4.gz tar.xz recovery.vfat", d)}' - # Boot artifacts to be copied from the deploy dir to the installer ZIP BOOTABLE_ARTIFACTS = " \ imx-boot-ccimx8x-sbc-pro-B0.bin \ diff --git a/meta-digi-arm/conf/machine/ccmp13-dvk.conf b/meta-digi-arm/conf/machine/ccmp13-dvk.conf index 0fad3d8de..61668bbd6 100644 --- a/meta-digi-arm/conf/machine/ccmp13-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp13-dvk.conf @@ -84,16 +84,6 @@ MACHINE_EXTRA_RRECOMMENDS += " \ ${MACHINE_FIRMWARE} \ " -# ========================================================================= -# Image -# ========================================================================= -# Image FS types -# tar.xz is omitted here because it's already being added via -# st-machine-common-stm32mp.inc in the meta-st-stm32mp layer -IMAGE_FSTYPES += '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ - "boot.ubifs recovery.ubifs squashfs", \ - "ubifs boot.ubifs recovery.ubifs", d)}' - # Default image for install scripts DEFAULT_IMAGE_NAME ?= "core-image-base" diff --git a/meta-digi-arm/conf/machine/ccmp15-dvk.conf b/meta-digi-arm/conf/machine/ccmp15-dvk.conf index 1f30afb6a..18b567fca 100644 --- a/meta-digi-arm/conf/machine/ccmp15-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp15-dvk.conf @@ -97,16 +97,6 @@ MACHINE_EXTRA_RRECOMMENDS += " \ ${MACHINE_FIRMWARE} \ " -# ========================================================================= -# Image -# ========================================================================= -# Image FS types -# tar.xz is omitted here because it's already being added via -# st-machine-common-stm32mp.inc in the meta-st-stm32mp layer -IMAGE_FSTYPES += '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ - "boot.ubifs recovery.ubifs squashfs", \ - "ubifs boot.ubifs recovery.ubifs", d)}' - # Default image for install scripts DEFAULT_IMAGE_NAME ?= "dey-image-webkit" diff --git a/meta-digi-arm/conf/machine/ccmp25-dvk.conf b/meta-digi-arm/conf/machine/ccmp25-dvk.conf index 124ec0a90..d7ab816b4 100644 --- a/meta-digi-arm/conf/machine/ccmp25-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp25-dvk.conf @@ -109,16 +109,6 @@ MACHINE_EXTRA_RRECOMMENDS += " \ ${MACHINE_FIRMWARE} \ " -# ========================================================================= -# Image -# ========================================================================= -# Image FS types -# tar.xz is omitted here because it's already being added via -# st-machine-common-stm32mp.inc in the meta-st-stm32mp layer -IMAGE_FSTYPES += '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ - "boot.vfat recovery.vfat squashfs", \ - "boot.vfat recovery.vfat ext4.gz", d)}' - # Default image for install scripts DEFAULT_IMAGE_NAME ?= "dey-image-webkit" diff --git a/meta-digi-arm/conf/machine/include/ccimx9.inc b/meta-digi-arm/conf/machine/include/ccimx9.inc index b2ed62b54..9fba16f9b 100644 --- a/meta-digi-arm/conf/machine/include/ccimx9.inc +++ b/meta-digi-arm/conf/machine/include/ccimx9.inc @@ -97,11 +97,6 @@ XBEE_RESET_N_GPIO ?= "gpio2@19" XBEE_SLEEP_RQ_GPIO ?= "" XBEE_TTY ?= "ttyLP3" -# Image FS types -IMAGE_FSTYPES ?= '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ - "boot.vfat recovery.vfat squashfs tar.xz", \ - "boot.vfat recovery.vfat ext4.gz tar.xz", d)}' - # Default overlayfs_etc mount point and type OVERLAYFS_ETC_MOUNT_POINT ?= "/mnt/data" OVERLAYFS_ETC_DEVICE ?= "/dev/mmcblk0p7" diff --git a/meta-digi-arm/conf/machine/include/ccmp1.inc b/meta-digi-arm/conf/machine/include/ccmp1.inc index 9260f2a37..f2d8f7166 100644 --- a/meta-digi-arm/conf/machine/include/ccmp1.inc +++ b/meta-digi-arm/conf/machine/include/ccmp1.inc @@ -77,7 +77,11 @@ MACHINE_EXTRA_RRECOMMENDS:remove = "kernel-imagebootfs" # ========================================================================= ALSA_LIST:stm32mp1common = "" -# Image FS types +# Image FS types (set by meta-st-stm32mp layer) +# tar.xz is omitted here because it's already added by ST layer +IMAGE_FSTYPES += '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ + "boot.ubifs recovery.ubifs squashfs", \ + "boot.ubifs recovery.ubifs ubifs", d)}' IMAGE_FSTYPES:remove = "ext4" IMAGE_FSTYPES:append = " ${@bb.utils.contains('BOOTDEVICE_LABELS', 'sdcard', 'boot.vfat ext4.gz', '', d)}" diff --git a/meta-digi-arm/conf/machine/include/ccmp2.inc b/meta-digi-arm/conf/machine/include/ccmp2.inc index 6929110f5..c8452ddbf 100644 --- a/meta-digi-arm/conf/machine/include/ccmp2.inc +++ b/meta-digi-arm/conf/machine/include/ccmp2.inc @@ -90,7 +90,11 @@ MACHINE_EXTRA_RDEPENDS += " \ # ========================================================================= ALSA_LIST:stm32mp2common = "" -# Image FS types +# Image FS types (set by meta-st-stm32mp layer) +# tar.xz is omitted here because it's already added by ST layer +IMAGE_FSTYPES += '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", \ + "boot.vfat recovery.vfat squashfs", \ + "boot.vfat recovery.vfat ext4.gz", d)}' IMAGE_FSTYPES:remove = "ext4" # ========================================================================= diff --git a/meta-digi-arm/conf/machine/include/digi-defaults.inc b/meta-digi-arm/conf/machine/include/digi-defaults.inc index 3d33a0032..bd7e5bfa2 100644 --- a/meta-digi-arm/conf/machine/include/digi-defaults.inc +++ b/meta-digi-arm/conf/machine/include/digi-defaults.inc @@ -115,3 +115,17 @@ BOOTLOADER_SEEK_BOOTPART ?= "0" # This is otherwise forced by systemd because of hard-coded paths # in systemd source code. ROOT_HOME ?= "/root" + +# Default IMAGE_FSTYPES for eMMC/NAND +IMAGE_FSTYPES ?= "${@ \ + oe.utils.conditional('STORAGE_MEDIA', 'mmc', \ + bb.utils.contains('IMAGE_FEATURES', 'read-only-rootfs', \ + 'boot.vfat recovery.vfat squashfs tar.xz', \ + 'boot.vfat recovery.vfat ext4.gz tar.xz', d), \ + oe.utils.conditional('STORAGE_MEDIA', 'mtd', \ + bb.utils.contains('IMAGE_FEATURES', 'read-only-rootfs', \ + 'boot.ubifs recovery.ubifs squashfs tar.xz', \ + 'boot.ubifs recovery.ubifs ubifs tar.xz', d), \ + '' , d), \ + d) \ +}" From fcd03fb4215cf3580261a85113b1d114b1f41321 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Mon, 16 Jun 2025 17:41:26 +0200 Subject: [PATCH 121/138] linux-dey: fix duplicate kernel localversion Yocto added kernel local version support by using the KERNEL_LOCALVERSION variable. It conflicts with the "fsl-kernel-localversion" class from the meta-freescale layer, resulting in a duplicated local version in the kernel release label (uname -r). Use the current Yocto support to avoid that duplication instead of meta-freescale's class. This enables further simplification of the 'copy_defconfig' task for NXP and STM platforms using common code. https://onedigi.atlassian.net/browse/DEL-9669 Signed-off-by: Javier Viguera --- .../recipes-kernel/linux/linux-dey.inc | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/meta-digi-arm/recipes-kernel/linux/linux-dey.inc b/meta-digi-arm/recipes-kernel/linux/linux-dey.inc index 755704788..fdcbb9e77 100644 --- a/meta-digi-arm/recipes-kernel/linux/linux-dey.inc +++ b/meta-digi-arm/recipes-kernel/linux/linux-dey.inc @@ -4,7 +4,6 @@ LICENSE = "GPL-2.0-only" LIC_FILES_CHKSUM = "file://COPYING;md5=6bc538ed5bd9a7fc9398086aedcd7e46" inherit kernel -inherit ${@oe.utils.conditional('DEY_SOC_VENDOR', 'NXP', 'fsl-kernel-localversion', '', d)} require ${@oe.utils.conditional('TRUSTFENCE_SIGN', '1', 'recipes-kernel/linux/linux-trustfence.inc', '', d)} # CONFIG_KERNEL_LZO in defconfig @@ -18,7 +17,7 @@ SRC_URI = " \ " S = "${WORKDIR}/git" -LOCALVERSION = "-dey" +KERNEL_LOCALVERSION = "-dey" # Create base DTB suitable for overlays OVERLAYS_DTC_FLAGS = "-@" @@ -43,22 +42,7 @@ do_copy_defconfig () { cp -f ${KERNEL_DEFCONFIG} ${WORKDIR}/defconfig fi } - -python () { - if d.getVar('DEY_SOC_VENDOR') == "STM": - bb.build.addtask('copy_defconfig', 'do_configure', 'do_patch', d) - elif d.getVar('DEY_SOC_VENDOR') == "NXP": - bb.build.addtask('copy_defconfig', 'do_kernel_localversion', 'do_patch', d) -} - -# This is needed because kernel_localversion (in fsl-kernel-localversion.bbclass) -# creates a basic ${B}/.config file and because that file exists, kernel_do_configure -# (in kernel.bbclass) does not apply our defconfig. -do_configure:prepend:imx-nxp-bsp() { - if [ -f "${WORKDIR}/defconfig" ] && [ -f "${B}/.config" ]; then - cat "${WORKDIR}/defconfig" >> "${B}/.config" - fi -} +addtask copy_defconfig before do_configure after do_patch # Apply configuration fragments do_configure:append() { From 93c6deb2d88ea0cd9520b0efb2390fc3c88fade9 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Tue, 3 Jun 2025 18:22:36 +0200 Subject: [PATCH 122/138] wolfcryptest: remove bbappend and FIPS patch The new version in meta-wolfssl does build properly, so this append is no longer needed. https://onedigi.atlassian.net/browse/DEL-9631 Signed-off-by: Javier Viguera --- .../wolfcrypt/wolfcrypttest.bbappend | 8 ----- ...fix-for-FIPS-enabled-wolfSSL-library.patch | 33 ------------------- 2 files changed, 41 deletions(-) delete mode 100644 meta-digi-arm/dynamic-layers/wolfssl/recipes-examples/wolfcrypt/wolfcrypttest.bbappend delete mode 100644 meta-digi-arm/dynamic-layers/wolfssl/recipes-examples/wolfcrypt/wolfcrypttest/0001-wolfcrypttest-fix-for-FIPS-enabled-wolfSSL-library.patch diff --git a/meta-digi-arm/dynamic-layers/wolfssl/recipes-examples/wolfcrypt/wolfcrypttest.bbappend b/meta-digi-arm/dynamic-layers/wolfssl/recipes-examples/wolfcrypt/wolfcrypttest.bbappend deleted file mode 100644 index b9bff6dfd..000000000 --- a/meta-digi-arm/dynamic-layers/wolfssl/recipes-examples/wolfcrypt/wolfcrypttest.bbappend +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (C) 2022, Digi International Inc. - -FILESEXTRAPATHS:prepend := "${THISDIR}/${BPN}:" - -SRC_URI:append = " file://0001-wolfcrypttest-fix-for-FIPS-enabled-wolfSSL-library.patch" - -CFLAGS:append:arm = " -DSIZEOF_LONG=4 -DSIZEOF_LONG_LONG=8" -CFLAGS:append:aarch64 = " -DSIZEOF_LONG=8 -DSIZEOF_LONG_LONG=8" diff --git a/meta-digi-arm/dynamic-layers/wolfssl/recipes-examples/wolfcrypt/wolfcrypttest/0001-wolfcrypttest-fix-for-FIPS-enabled-wolfSSL-library.patch b/meta-digi-arm/dynamic-layers/wolfssl/recipes-examples/wolfcrypt/wolfcrypttest/0001-wolfcrypttest-fix-for-FIPS-enabled-wolfSSL-library.patch deleted file mode 100644 index 15a038045..000000000 --- a/meta-digi-arm/dynamic-layers/wolfssl/recipes-examples/wolfcrypt/wolfcrypttest/0001-wolfcrypttest-fix-for-FIPS-enabled-wolfSSL-library.patch +++ /dev/null @@ -1,33 +0,0 @@ -From: Javier Viguera -Date: Thu, 1 Sep 2022 18:58:13 +0200 -Subject: [PATCH] wolfcrypttest: fix for FIPS enabled wolfSSL library - -FIPS does not support MD5 - -Upstream-Status: Inappropriate [DEY specific] - -Signed-off-by: Javier Viguera ---- - test.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/test.c b/test.c -index 8ad937a8bacb..00d8ba0d2503 100644 ---- a/test.c -+++ b/test.c -@@ -18370,9 +18370,15 @@ WOLFSSL_TEST_SUBROUTINE int openssl_test(void) - c.inLen = XSTRLEN(c.input); - c.outLen = WC_MD5_DIGEST_SIZE; - -+#if defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION > 2) -+ /* Expect failure with MD5 + HMAC when using FIPS 140-3. */ -+ if (HMAC(EVP_md5(), "JefeJefeJefeJefe", 16, (byte*)c.input, (int)c.inLen, -+ hash, 0) != NULL) -+#else - if (HMAC(EVP_md5(), "JefeJefeJefeJefe", 16, (byte*)c.input, (int)c.inLen, - hash, 0) == NULL || - XMEMCMP(hash, c.output, WC_MD5_DIGEST_SIZE) != 0) -+#endif - { - return -8612; - } From 78a1e7864fe23d7d4f12658c5d79126605f5e5a1 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Tue, 3 Jun 2025 18:23:06 +0200 Subject: [PATCH 123/138] meta-digi-arm: remove old WolfSSL FIPS support * Delete custom wolfssl_5.4.0-fips.bb recipe and README. * Removed WolfSSL dynamic layer registration. FIPS support is now managed through the external meta-wolfssl layer, making this implementation unnecessary in meta-digi. https://onedigi.atlassian.net/browse/DEL-9631 Signed-off-by: Javier Viguera --- meta-digi-arm/conf/layer.conf | 2 - .../recipes-wolfssl/wolfssl/README.fips | 139 ------------------ .../wolfssl/wolfssl_5.4.0-fips.bb | 73 --------- 3 files changed, 214 deletions(-) delete mode 100644 meta-digi-arm/dynamic-layers/wolfssl/recipes-wolfssl/wolfssl/README.fips delete mode 100644 meta-digi-arm/dynamic-layers/wolfssl/recipes-wolfssl/wolfssl/wolfssl_5.4.0-fips.bb diff --git a/meta-digi-arm/conf/layer.conf b/meta-digi-arm/conf/layer.conf index ad128ea0d..5e71ab783 100644 --- a/meta-digi-arm/conf/layer.conf +++ b/meta-digi-arm/conf/layer.conf @@ -14,8 +14,6 @@ BBFILES_DYNAMIC += " \ freescale-layer:${LAYERDIR}/dynamic-layers/freescale-layer/*/*/*.bbappend \ stm-st-stm32mp:${LAYERDIR}/dynamic-layers/stm-st-stm32mp/*/*/*.bb \ stm-st-stm32mp:${LAYERDIR}/dynamic-layers/stm-st-stm32mp/*/*/*.bbappend \ - wolfssl:${LAYERDIR}/dynamic-layers/wolfssl/*/*/*.bb \ - wolfssl:${LAYERDIR}/dynamic-layers/wolfssl/*/*/*.bbappend \ " LAYERDEPENDS_digi-arm = "core" diff --git a/meta-digi-arm/dynamic-layers/wolfssl/recipes-wolfssl/wolfssl/README.fips b/meta-digi-arm/dynamic-layers/wolfssl/recipes-wolfssl/wolfssl/README.fips deleted file mode 100644 index 4b5cbef54..000000000 --- a/meta-digi-arm/dynamic-layers/wolfssl/recipes-wolfssl/wolfssl/README.fips +++ /dev/null @@ -1,139 +0,0 @@ -Digi Embedded Yocto FIPS-certified WolfSSL support -================================================== - -WolfSSL is a lightweight SSL/TLS library written in C and targeted for -embedded and resource-constrained environments. - -WolfSSL is powered by the wolfCrypt library. A version of the wolfCrypt -library has been FIPS 140-2 validated, with FIPS 140-3 validation currently -in progress. - -For more information, visit: -https://www.wolfssl.com/license/fips/ - -DEY support ------------ -Digi Embedded Yocto (DEY) supports building the FIPS validated version of -WolfSSL. The source package is usually provided under a commercial license -agreement by WolfSSL. DEY provides the recipes and configurations to build -the recipe into your final image. - -Instructions ------------- -These instructions assume that DEY is properly installed and a project -has already been created. For more info on those tasks, see the online -documentation on the Digi Embedded Documentation portal: - -https://www.digi.com/resources/documentation/digidocs/embedded/index.html - -1. Add 'meta-wolfssl' layer to the project. - - # cd - # bitbake-layers add-layer /sources/meta-wolfssl - -2. Configure the project for building wolfssl FIPS bundle (1st build). - - DEY added support for building the library from a password-protected - 7z-compressed package, but any other package format supported by Yocto may - be used. - - The build is controlled by variables configured in the project's - configuration file (/conf/local.conf). - - * PREFERRED_VERSION_wolfssl: the version of the wolfssl recipe to build - * WOLFSSL_FIPS_PKG_PATH: absolute local path to the package - * WOLFSSL_FIPS_PKG_PASSWORD: package's unpack password (only for 7z compression) - * WOLFSSL_FIPS_CORE_HASH: in-core integrity hash (not available until - after a first run) - - An example follows: - - PREFERRED_VERSION_wolfssl = "5.4.0-fips" - WOLFSSL_FIPS_PKG_PATH = "/PATH/TO/wolfssl-5.4.0-commercial-fips-linuxv5.7z" - WOLFSSL_FIPS_PKG_PASSWORD = "xxxx" - #WOLFSSL_FIPS_CORE_HASH = "" - - Note: Leave the variable WOLFSSL_FIPS_CORE_HASH commented. The `wolfcrypttest` - application provides the WolfSSL FIPS integrity hash value after the first run. - -3. Add the wolfCrypt test programs to the image. - - In the project's configuration file: - - IMAGE_INSTALL:append = " wolfssl wolfcrypttest wolfcryptbenchmark" - -4. Build and program the images in the device. - - If you need more information on this topic, refer to the DEY online - documentation (link above). - -5. Compute the WolfSSL FIPS integrity hash. - - In the device, run the `wolfcrypttest` test application. At this point, - it is expected that the application fails because the library has not been - built with the integrity hash. - - root:~# wolfcrypttest - ------------------------------------------------------------------------------ - wolfSSL version 5.4.0 - ------------------------------------------------------------------------------ - error test passed! - MEMORY test passed! - base64 test passed! - base16 test passed! - asn test passed! - in my Fips callback, ok = 0, err = -203 - message = In Core Integrity check FIPS error - hash = 9490AAFD1786A11115256841AA71F9B5313BAA244ACF1A07DD8BB8A893CBC5BC - In core integrity hash check failure, copy above hash - into verifyCore[] in fips_test.c and rebuild - RANDOM test failed! - error = -7000 - Exiting main with return code: -1 - -6. Reconfigure the project and build the images again (2nd build). - - Feed the FIPS integrity hash back into the build process with the - WOLFSSL_FIPS_CORE_HASH variable. - - For example, the final configuration would be: - - PREFERRED_VERSION_wolfssl = "5.4.0-fips" - WOLFSSL_FIPS_PKG_PATH = "/PATH/TO/wolfssl-5.4.0-commercial-fips-linuxv5.7z" - WOLFSSL_FIPS_PKG_PASSWORD = "xxxx" - WOLFSSL_FIPS_CORE_HASH = "9490AAFD1786A11115256841AA71F9B5313BAA244ACF1A07DD8BB8A893CBC5BC" - - IMAGE_INSTALL:append = " wolfssl wolfcrypttest wolfcryptbenchmark" - - Make sure you get rid of the old build objects and rebuild the images: - - # bitbake -c cleansstate wolfssl wolfcrypttest wolfcryptbenchmark - # bitbake -c cleanall - -7. Build and program the images in the device again. - - Now the test application should complete just fine: - - root:~# wolfcrypttest - ------------------------------------------------------------------------------ - wolfSSL version 5.4.0 - ------------------------------------------------------------------------------ - error test passed! - MEMORY test passed! - base64 test passed! - base16 test passed! - asn test passed! - RANDOM test passed! - MD5 test passed! - SHA test passed! - SHA-224 test passed! - ... - PKCS7authenveloped test passed! - prime test passed! - logging test passed! - time test passed! - mutex test passed! - memcb test passed! - crypto callback test passed! - Test complete - Exiting main with return code: 0 diff --git a/meta-digi-arm/dynamic-layers/wolfssl/recipes-wolfssl/wolfssl/wolfssl_5.4.0-fips.bb b/meta-digi-arm/dynamic-layers/wolfssl/recipes-wolfssl/wolfssl/wolfssl_5.4.0-fips.bb deleted file mode 100644 index 5898114fd..000000000 --- a/meta-digi-arm/dynamic-layers/wolfssl/recipes-wolfssl/wolfssl/wolfssl_5.4.0-fips.bb +++ /dev/null @@ -1,73 +0,0 @@ -SUMMARY = "wolfSSL Lightweight Embedded SSL/TLS Library" -DESCRIPTION = "wolfSSL is a lightweight SSL/TLS library written in C and \ - optimized for embedded and RTOS environments. It can be up \ - to 20 times smaller than OpenSSL while still supporting \ - a full TLS client and server, up to TLS 1.3" -HOMEPAGE = "https://www.wolfssl.com/products/wolfssl" -BUGTRACKER = "https://github.com/wolfssl/wolfssl/issues" -SECTION = "libs" -LICENSE = "WolfSSL-Commercial" -LICENSE_FLAGS = "commercial" -LIC_FILES_CHKSUM = "file://WolfSSL_LicenseAgmt_JAN-2022.pdf;md5=be28609dc681e98236c52428fadf04dd" -NO_GENERIC_LICENSE[WolfSSL-Commercial] = "WolfSSL_LicenseAgmt_JAN-2022.pdf" - -PROVIDES += "cyassl" -RPROVIDES:${PN} = "cyassl" -PROVIDES += "wolfssl" -RPROVIDES:${PN} = "wolfssl" - -# To be configured in project's config file -WOLFSSL_FIPS_PKG_NAME ?= "wolfssl-5.4.0-commercial-fips-linuxv5" -WOLFSSL_FIPS_PKG_PASSWORD ?= "" -WOLFSSL_FIPS_PKG_PATH ?= "" - -python() { - # The package is not publicly available, so provide a PREMIRROR to a local directory - # that can be configured in the project's local.conf file using WOLFSSL_FIPS_PKG_PATH - # variable. - wolfssl_fips_local_path = d.getVar('WOLFSSL_FIPS_PKG_PATH') - if wolfssl_fips_local_path: - premirrors = d.getVar('PREMIRRORS') - d.setVar('PREMIRRORS', "http:///not/exist/${WOLFSSL_FIPS_PKG_NAME}.7z file://%s \\n %s" % (wolfssl_fips_local_path, premirrors)) - - # Yocto does not support unpacking password protected packages, so configure the - # SRC_URI as unpack=false in that case. - d.setVar('WOLFSSL_FIPS_PKG_UNPACK', str(not d.getVar('WOLFSSL_FIPS_PKG_PASSWORD'))) - - # Aux variable to prevent running 7za archiver on a not-7z package - d.setVar('WOLFSSL_FIPS_PKG_IS_7Z', str(d.getVar('WOLFSSL_FIPS_PKG_PATH').endswith('.7z'))) - - # FIPS core integrity hash needs to be added back to build process - wolfssl_fips_core_hash = d.getVar('WOLFSSL_FIPS_CORE_HASH') - if wolfssl_fips_core_hash: - d.setVar('CFLAGS:append', " -DWOLFCRYPT_FIPS_CORE_HASH_VALUE=%s" % wolfssl_fips_core_hash) -} - -SRC_URI = "http:///not/exist/${WOLFSSL_FIPS_PKG_NAME}.7z;unpack=${WOLFSSL_FIPS_PKG_UNPACK}" -SRC_URI[sha256sum] = "0743e481e9e3ec2b7ba531c5821c44d55b313c0af04ded148caf4db7e0baa582" - -S = "${WORKDIR}/${WOLFSSL_FIPS_PKG_NAME}" - -inherit autotools - -do_unpack[depends] += "p7zip-native:do_populate_sysroot" -do_unpack[postfuncs] += "${@oe.utils.vartrue('WOLFSSL_FIPS_PKG_UNPACK', '', 'unpack_7z_password_pkg', d)}" -unpack_7z_password_pkg() { - if [ "${WOLFSSL_FIPS_PKG_IS_7Z}" = "True" ]; then - 7za x -o${WORKDIR} -p${WOLFSSL_FIPS_PKG_PASSWORD} -y ${WORKDIR}/${WOLFSSL_FIPS_PKG_NAME}.7z 1>/dev/null - fi -} - -# Enable FIPS support, the compatibility layer and some other useful options -EXTRA_OECONF += " \ - --enable-fips=v5 \ - --enable-opensslextra \ - --enable-postauth \ - --enable-sha3 \ - --enable-tls13 \ - --enable-tlsx \ -" - -BBCLASSEXTEND += "native nativesdk" - -DEFAULT_PREFERENCE = "-1" From bd71ef76e5f5320694e1509a8f8070f6bb2d6629 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Tue, 17 Jun 2025 16:56:50 +0200 Subject: [PATCH 124/138] build scripts: limit sdcard image to platforms that support it The CC6UL SOM doesn't support booting from the SD card. The STM based SOMs use a wic template to generate the SD card image. Signed-off-by: Hector Palacios --- sdk/build-github.sh | 5 ++++- sdk/build.sh | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/sdk/build-github.sh b/sdk/build-github.sh index f2a3ef172..099d2479c 100755 --- a/sdk/build-github.sh +++ b/sdk/build-github.sh @@ -37,7 +37,10 @@ DEY_IMAGE_INSTALLER = \"1\" " SDCARD_FSTYPE=" -IMAGE_FSTYPES:append = \" sdcard.gz\" +IMAGE_FSTYPES:append:ccimx6 = \" sdcard.gz\" +IMAGE_FSTYPES:append:ccimx8x = \" sdcard.gz\" +IMAGE_FSTYPES:append:ccimx8m = \" sdcard.gz\" +IMAGE_FSTYPES:append:ccimx9 = \" sdcard.gz\" " SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(date +%s)}" diff --git a/sdk/build.sh b/sdk/build.sh index fb83dbbfa..6d7d59fca 100755 --- a/sdk/build.sh +++ b/sdk/build.sh @@ -59,7 +59,10 @@ DEY_IMAGE_INSTALLER = \"1\" " SDCARD_FSTYPE=" -IMAGE_FSTYPES:append = \" sdcard.gz\" +IMAGE_FSTYPES:append:ccimx6 = \" sdcard.gz\" +IMAGE_FSTYPES:append:ccimx8x = \" sdcard.gz\" +IMAGE_FSTYPES:append:ccimx8m = \" sdcard.gz\" +IMAGE_FSTYPES:append:ccimx9 = \" sdcard.gz\" " SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(date +%s)}" From 5947b0cc9c4c765f988148f0a42ca6e245b57ea3 Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Thu, 12 Jun 2025 11:19:58 +0200 Subject: [PATCH 125/138] vsftpd: add support for SSL/TLS encrypted FTP sessions This commit enables SSL/TLS support in vsftpd, allowing FTP communications to be encrypted for improved security. SSL/TLS support is enabled by default, but to preserve compatibility with clients that do not support encrypted connections, this feature can be disabled at runtime. Users can simply comment or uncomment a few lines in the `vsftpd.conf` configuration file to toggle the behavior. Signed-off-by: Arturo Buzarra --- ...001-builddefs-add-support-to-OpenSSL.patch | 23 ++++++ .../vsftpd/vsftpd/vsftpd-cert | 12 ++++ .../vsftpd/vsftpd/vsftpd-cert.service | 11 +++ .../vsftpd/vsftpd/vsftpd.conf | 9 +++ .../vsftpd/vsftpd_%.bbappend | 70 ++++++++++++++++++- 5 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 meta-digi-dey/recipes-connectivity/vsftpd/vsftpd/0001-builddefs-add-support-to-OpenSSL.patch create mode 100644 meta-digi-dey/recipes-connectivity/vsftpd/vsftpd/vsftpd-cert create mode 100644 meta-digi-dey/recipes-connectivity/vsftpd/vsftpd/vsftpd-cert.service diff --git a/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd/0001-builddefs-add-support-to-OpenSSL.patch b/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd/0001-builddefs-add-support-to-OpenSSL.patch new file mode 100644 index 000000000..7faec7c6f --- /dev/null +++ b/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd/0001-builddefs-add-support-to-OpenSSL.patch @@ -0,0 +1,23 @@ +From: Arturo Buzarra +Date: Wed, 4 Jun 2025 09:15:27 +0200 +Subject: [PATCH] builddefs: add support to OpenSSL + +Signed-off-by: Arturo Buzarra +--- + builddefs.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/builddefs.h b/builddefs.h +index 0106d1a..83de674 100644 +--- a/builddefs.h ++++ b/builddefs.h +@@ -1,9 +1,9 @@ + #ifndef VSF_BUILDDEFS_H + #define VSF_BUILDDEFS_H + + #define VSF_BUILD_TCPWRAPPERS + #define VSF_BUILD_PAM +-#undef VSF_BUILD_SSL ++#define VSF_BUILD_SSL + + #endif /* VSF_BUILDDEFS_H */ diff --git a/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd/vsftpd-cert b/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd/vsftpd-cert new file mode 100644 index 000000000..e7a2ab9cf --- /dev/null +++ b/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd/vsftpd-cert @@ -0,0 +1,12 @@ +#!/bin/sh + +# Generate self-signed certificate and key if they don't already exist +if [ ! -f "/etc/##VSFTPD_PEM##" ] || [ ! -f "/etc/##VSFTPD_KEY##" ]; then + openssl req ##VSFTPD_KEY_SIGN_PKCS## ##VSFTPD_KEY_REQ_ARGS## \ + -out /etc/##VSFTPD_PEM## \ + -keyout /etc/##VSFTPD_KEY## + chmod 400 /etc/##VSFTPD_KEY## + echo "SSL/TLS certificate generated" +else + echo "SSL/TLS certificate already exists" +fi diff --git a/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd/vsftpd-cert.service b/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd/vsftpd-cert.service new file mode 100644 index 000000000..e44c52908 --- /dev/null +++ b/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd/vsftpd-cert.service @@ -0,0 +1,11 @@ +[Unit] +Description=Vsftpd ftp daemon certificate generation +Before=vsftpd.service +ConditionPathExists=!/etc/##VSFTPD_PEM## + +[Service] +Type=oneshot +ExecStart=/etc/vsftpd-cert + +[Install] +WantedBy=multi-user.target diff --git a/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd/vsftpd.conf b/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd/vsftpd.conf index ac1ed380b..b322459e6 100644 --- a/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd/vsftpd.conf +++ b/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd/vsftpd.conf @@ -18,3 +18,12 @@ connect_from_port_20=YES file_open_mode=0777 local_umask=022 anon_umask=022 + +# These options specifies the location of the RSA certificate to use for SSL +# encrypted connections. +ssl_enable=YES +rsa_cert_file=/etc/##VSFTPD_PEM## +rsa_private_key_file=/etc/##VSFTPD_KEY## +# SSL session reuse is enabled by default to enhance security. +# Set to NO if clients have issues with data connections. +#require_ssl_reuse=NO diff --git a/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd_%.bbappend b/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd_%.bbappend index dba7c4e2f..b14c7b09d 100644 --- a/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd_%.bbappend +++ b/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd_%.bbappend @@ -1,7 +1,28 @@ -# Copyright (C) 2013-2019, Digi International Inc. +# Copyright (C) 2013-2025, Digi International Inc. FILESEXTRAPATHS:prepend := "${THISDIR}/${BPN}:" +SRC_URI:append = " \ + ${@bb.utils.contains('PACKAGECONFIG', 'openssl', 'file://0001-builddefs-add-support-to-OpenSSL.patch', '', d)} \ + ${@bb.utils.contains('PACKAGECONFIG', 'openssl', 'file://vsftpd-cert', '', d)} \ + ${@bb.utils.contains('PACKAGECONFIG', 'openssl', 'file://vsftpd-cert.service', '', d)} \ +" + +RDEPENDS:${PN}:append = "${@bb.utils.contains('PACKAGECONFIG', 'openssl', ' ${PN}-cert', '', d)}" + +PACKAGECONFIG:append = " openssl " +PACKAGECONFIG[openssl] = ",,openssl" + +LDFLAGS += "${@bb.utils.contains('PACKAGECONFIG', 'openssl', '-lssl -lcrypto', '', d)}" + +VSFTPD_PEM ?= "vsftpd.pem" +VSFTPD_KEY ?= "vsftpd.key" +# args to openssl req (Default is -batch for non interactive mode and +# -newkey for new certificate) +VSFTPD_KEY_REQ_ARGS ?= "-nodes -batch -newkey rsa:2048" +# Standard format for public key certificate +VSFTPD_KEY_SIGN_PKCS ?= "-x509" + do_install:append() { if ! test -z "${PAMLIB}" ; then # Access through Pluggable Authentication Modules (PAM) @@ -12,4 +33,51 @@ do_install:append() { echo "d /run/vsftpd/empty 0755 root root -" \ > ${D}${sysconfdir}/tmpfiles.d/${BPN}.conf fi + if ${@bb.utils.contains('PACKAGECONFIG', 'openssl', 'true', 'false', d)}; then + VSFTPD_PEM_BASE_NAME=$(basename ${VSFTPD_PEM}) + VSFTPD_KEY_BASE_NAME=$(basename ${VSFTPD_KEY}) + # Install user certificate if provided + if [ -f "${VSFTPD_PEM}" ] && [ -f "${VSFTPD_KEY}" ]; then + install -m 0644 ${VSFTPD_PEM} ${D}${sysconfdir}/${VSFTPD_PEM_BASE_NAME} + install -m 0400 ${VSFTPD_KEY} ${D}${sysconfdir}/${VSFTPD_KEY_BASE_NAME} + fi + + # Install systemd service + if ${@bb.utils.contains('DISTRO_FEATURES', 'systemd', 'true', 'false', d)}; then + # Install systemd unit files + install -d ${D}${systemd_unitdir}/system + install -m 0644 ${WORKDIR}/vsftpd-cert.service ${D}${systemd_unitdir}/system/ + sed -i -e "s@##VSFTPD_PEM##@${VSFTPD_PEM_BASE_NAME}@g" \ + "${D}${systemd_unitdir}/system/vsftpd-cert.service" + fi + + # Install init script to generate certificate on target + install -d ${D}${sysconfdir}/init.d + install -m 0755 ${WORKDIR}/vsftpd-cert ${D}${sysconfdir}/vsftpd-cert + sed -i -e "s@##VSFTPD_PEM##@${VSFTPD_PEM_BASE_NAME}@g" \ + -e "s@##VSFTPD_KEY##@${VSFTPD_KEY_BASE_NAME}@g" \ + -e "s@##VSFTPD_KEY_SIGN_PKCS##@${VSFTPD_KEY_SIGN_PKCS}@g" \ + -e "s@##VSFTPD_KEY_REQ_ARGS##@${VSFTPD_KEY_REQ_ARGS}@g" \ + "${D}${sysconfdir}/vsftpd-cert" + ln -sf ${sysconfdir}/vsftpd-cert ${D}${sysconfdir}/init.d/vsftpd-cert + + # Customize vsftpd.conf + sed -i -e "s@##VSFTPD_PEM##@${VSFTPD_PEM_BASE_NAME}@g" \ + -e "s@##VSFTPD_KEY##@${VSFTPD_KEY_BASE_NAME}@g" \ + "${D}${sysconfdir}/vsftpd.conf" + fi } + +PACKAGES =+ "${PN}-cert" +FILES:${PN}-cert = " \ + ${sysconfdir}/vsftpd-cert \ + ${sysconfdir}/init.d/vsftpd-cert \ + ${systemd_unitdir}/system/vsftpd-cert.service \ +" + +INITSCRIPT_PACKAGES += "${@bb.utils.contains('PACKAGECONFIG', 'openssl', '${PN}-cert', '', d)}" +INITSCRIPT_NAME:${PN}-cert = "vsftpd-cert" +INITSCRIPT_PARAMS:${PN}-cert = "start 99 3 5 . stop 20 0 1 2 6 ." + +SYSTEMD_PACKAGES += "${@bb.utils.contains('PACKAGECONFIG', 'openssl', '${PN}-cert', '', d)}" +SYSTEMD_SERVICE:${PN}-cert = "vsftpd-cert.service" From f3c9b3c57a146b3155c27ae9e8e5e934d7ae6d69 Mon Sep 17 00:00:00 2001 From: Gonzalo Ruiz Date: Thu, 19 Jun 2025 10:59:04 +0200 Subject: [PATCH 126/138] weston-init: wait for a wayland socket before logging in The login prompt appears before Wayland is fully initialized and has created a wayland socket. Logging in too early as root in this scenario caused the WAYLAND_DISPLAY environment variable to be left empty. As a consequence, gstreamer failed to use waylandsink to print contents in the display. Introduce a 10-seconds polling loop to wait for the wayland socket to be available before proceeding with the login. Signed-off-by: Gonzalo Ruiz --- .../wayland/weston-init/weston_profile.sh | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-graphics/wayland/weston-init/weston_profile.sh b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-graphics/wayland/weston-init/weston_profile.sh index 308452ec8..5efd2b5bc 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-graphics/wayland/weston-init/weston_profile.sh +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-graphics/wayland/weston-init/weston_profile.sh @@ -8,11 +8,20 @@ if [ "$USER" == "root" ]; then export PULSE_RUNTIME_PATH=/run/user/`id -u root`/pulse export USE_PLAYBIN3=1 - if [ -e $XDG_RUNTIME_DIR/wayland-0 ]; then - export WAYLAND_DISPLAY=wayland-0 - else - if [ -e $XDG_RUNTIME_DIR/wayland-1 ]; then - export WAYLAND_DISPLAY=wayland-1 - fi + # Wait for 10 seconds until a Wayland socket is available + for i in {1..10}; do + if [ -e $XDG_RUNTIME_DIR/wayland-0 ]; then + export WAYLAND_DISPLAY=wayland-0 + break + elif [ -e $XDG_RUNTIME_DIR/wayland-1 ]; then + export WAYLAND_DISPLAY=wayland-1 + break + else + sleep 1 + fi + done + + if [ -z "$WAYLAND_DISPLAY" ]; then + echo "WARNING: No Wayland socket found" fi fi From 66c39a590dc0807acafff863c68016e4d7d56ac6 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Thu, 19 Jun 2025 17:46:19 +0200 Subject: [PATCH 127/138] wic: ccmp1: update TF-A and FIP artifact names to DEY-5.0 - The TF-A binaries now have ${BOOTSCHEME_DEFAULT} in them (optee or opteemin) - The FIP binaries now have ${BOOTSCHEME_DEFAULT}-sdcard in them Signed-off-by: Hector Palacios https://onedigi.atlassian.net/browse/DEL-9678 --- meta-digi-arm/wic/ccmp1-1GB.wks.in | 8 ++++---- meta-digi-arm/wic/ccmp1-256MB.wks.in | 8 ++++---- meta-digi-arm/wic/ccmp1-512MB.wks.in | 8 ++++---- meta-digi-arm/wic/ccmp1-signed-1GB.wks.in | 8 ++++---- meta-digi-arm/wic/ccmp1-signed-256MB.wks.in | 12 ++++++------ meta-digi-arm/wic/ccmp1-signed-512MB.wks.in | 8 ++++---- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/meta-digi-arm/wic/ccmp1-1GB.wks.in b/meta-digi-arm/wic/ccmp1-1GB.wks.in index 417b4946b..9f2d86544 100644 --- a/meta-digi-arm/wic/ccmp1-1GB.wks.in +++ b/meta-digi-arm/wic/ccmp1-1GB.wks.in @@ -12,16 +12,16 @@ # # FSBL partitions aka TF-A BL2 -part fsbl1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-1GB-sdcard.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K --align 17 -part fsbl2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-1GB-sdcard.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K +part fsbl1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-1GB-${BOOTSCHEME_DEFAULT}-sdcard.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K --align 17 +part fsbl2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-1GB-${BOOTSCHEME_DEFAULT}-sdcard.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K # Metadata partitions part metadata1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=metadata1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/metadata.bin" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K part metadata2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=metadata2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/metadata.bin" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K # Fip partitions -part fip-a --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-a --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-1GB-optee.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 4fd84c93-54ef-463f-a7ef-ae25ff887087 -part fip-b --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-b --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-1GB-optee.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 09c54952-d5bf-45af-acee-335303766fb3 +part fip-a --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-a --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-1GB-${BOOTSCHEME_DEFAULT}-sdcard.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 4fd84c93-54ef-463f-a7ef-ae25ff887087 +part fip-b --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-b --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-1GB-${BOOTSCHEME_DEFAULT}-sdcard.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 09c54952-d5bf-45af-acee-335303766fb3 # U-BOOT env part u-boot-env --source empty --part-name=uboot-env --ondisk mmcblk --part-type 0x8301 --fixed-size 512K diff --git a/meta-digi-arm/wic/ccmp1-256MB.wks.in b/meta-digi-arm/wic/ccmp1-256MB.wks.in index f11997d57..a7d5ad3b2 100644 --- a/meta-digi-arm/wic/ccmp1-256MB.wks.in +++ b/meta-digi-arm/wic/ccmp1-256MB.wks.in @@ -12,16 +12,16 @@ # # FSBL partitions aka TF-A BL2 -part fsbl1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-256MB-sdcard.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K --align 17 -part fsbl2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-256MB-sdcard.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K +part fsbl1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-256MB-${BOOTSCHEME_DEFAULT}-sdcard.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K --align 17 +part fsbl2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-256MB-${BOOTSCHEME_DEFAULT}-sdcard.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K # Metadata partitions part metadata1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=metadata1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/metadata.bin" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K part metadata2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=metadata2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/metadata.bin" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K # Fip partitions -part fip-a --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-a --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-256MB-optee.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 4fd84c93-54ef-463f-a7ef-ae25ff887087 -part fip-b --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-b --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-256MB-optee.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 09c54952-d5bf-45af-acee-335303766fb3 +part fip-a --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-a --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-256MB-${BOOTSCHEME_DEFAULT}-sdcard.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 4fd84c93-54ef-463f-a7ef-ae25ff887087 +part fip-b --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-b --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-256MB-${BOOTSCHEME_DEFAULT}-sdcard.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 09c54952-d5bf-45af-acee-335303766fb3 # U-BOOT env part u-boot-env --source empty --part-name=uboot-env --ondisk mmcblk --part-type 0x8301 --fixed-size 512K diff --git a/meta-digi-arm/wic/ccmp1-512MB.wks.in b/meta-digi-arm/wic/ccmp1-512MB.wks.in index 11357d0b2..624e6e341 100644 --- a/meta-digi-arm/wic/ccmp1-512MB.wks.in +++ b/meta-digi-arm/wic/ccmp1-512MB.wks.in @@ -12,16 +12,16 @@ # # FSBL partitions aka TF-A BL2 -part fsbl1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-512MB-sdcard.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K --align 17 -part fsbl2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-512MB-sdcard.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K +part fsbl1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-512MB-${BOOTSCHEME_DEFAULT}-sdcard.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K --align 17 +part fsbl2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-512MB-${BOOTSCHEME_DEFAULT}-sdcard.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K # Metadata partitions part metadata1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=metadata1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/metadata.bin" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K part metadata2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=metadata2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/metadata.bin" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K # Fip partitions -part fip-a --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-a --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-512MB-optee.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 4fd84c93-54ef-463f-a7ef-ae25ff887087 -part fip-b --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-b --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-512MB-optee.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 09c54952-d5bf-45af-acee-335303766fb3 +part fip-a --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-a --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-512MB-${BOOTSCHEME_DEFAULT}-sdcard.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 4fd84c93-54ef-463f-a7ef-ae25ff887087 +part fip-b --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-b --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-512MB-${BOOTSCHEME_DEFAULT}-sdcard.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 09c54952-d5bf-45af-acee-335303766fb3 # U-BOOT env part u-boot-env --source empty --part-name=uboot-env --ondisk mmcblk --part-type 0x8301 --fixed-size 512K diff --git a/meta-digi-arm/wic/ccmp1-signed-1GB.wks.in b/meta-digi-arm/wic/ccmp1-signed-1GB.wks.in index 7ff966d5d..27744f6d6 100644 --- a/meta-digi-arm/wic/ccmp1-signed-1GB.wks.in +++ b/meta-digi-arm/wic/ccmp1-signed-1GB.wks.in @@ -12,16 +12,16 @@ # # FSBL partitions aka TF-A BL2 -part fsbl1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-1GB-sdcard.stm32_signed" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K --align 17 -part fsbl2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-1GB-sdcard.stm32_signed" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K +part fsbl1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-1GB-${BOOTSCHEME_DEFAULT}-sdcard_Signed.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K --align 17 +part fsbl2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-1GB-${BOOTSCHEME_DEFAULT}-sdcard_Signed.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K # Metadata partitions part metadata1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=metadata1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/metadata.bin" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K part metadata2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=metadata2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/metadata.bin" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K # Fip partitions -part fip-a --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-a --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-1GB-optee_Signed.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 4fd84c93-54ef-463f-a7ef-ae25ff887087 -part fip-b --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-b --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-1GB-optee_Signed.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 09c54952-d5bf-45af-acee-335303766fb3 +part fip-a --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-a --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-1GB-${BOOTSCHEME_DEFAULT}-sdcard_Signed.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 4fd84c93-54ef-463f-a7ef-ae25ff887087 +part fip-b --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-b --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-1GB-${BOOTSCHEME_DEFAULT}-sdcard_Signed.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 09c54952-d5bf-45af-acee-335303766fb3 # U-BOOT env part u-boot-env --source empty --part-name=uboot-env --ondisk mmcblk --part-type 0x8301 --fixed-size 512K diff --git a/meta-digi-arm/wic/ccmp1-signed-256MB.wks.in b/meta-digi-arm/wic/ccmp1-signed-256MB.wks.in index 703f36797..319192167 100644 --- a/meta-digi-arm/wic/ccmp1-signed-256MB.wks.in +++ b/meta-digi-arm/wic/ccmp1-signed-256MB.wks.in @@ -1,5 +1,5 @@ -# short-description: Create SD card image with a boot partition (1GB) -# long-description: Creates a partitioned SD card image (1GB) +# short-description: Create signed SD card image with a boot partition (1GB) +# long-description: Creates a partitioned SD card image with signed artifacts (1GB) # # - -------- ------------- ------ ------ ------------ -------- --------- -------- # | | TFA(2) | Metadata(2) | FIPA | FIPB | U-BOOT ENV | linux | rootfs | data | @@ -12,16 +12,16 @@ # # FSBL partitions aka TF-A BL2 -part fsbl1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-256MB-sdcard.stm32_signed" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K --align 17 -part fsbl2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-256MB-sdcard.stm32_signed" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K +part fsbl1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-256MB-${BOOTSCHEME_DEFAULT}-sdcard_Signed.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K --align 17 +part fsbl2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-256MB-${BOOTSCHEME_DEFAULT}-sdcard_Signed.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K # Metadata partitions part metadata1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=metadata1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/metadata.bin" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K part metadata2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=metadata2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/metadata.bin" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K # Fip partitions -part fip-a --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-a --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-256MB-optee_Signed.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 4fd84c93-54ef-463f-a7ef-ae25ff887087 -part fip-b --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-b --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-256MB-optee_Signed.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 09c54952-d5bf-45af-acee-335303766fb3 +part fip-a --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-a --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-256MB-${BOOTSCHEME_DEFAULT}-sdcard_Signed.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 4fd84c93-54ef-463f-a7ef-ae25ff887087 +part fip-b --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-b --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-256MB-${BOOTSCHEME_DEFAULT}-sdcard_Signed.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 09c54952-d5bf-45af-acee-335303766fb3 # U-BOOT env part u-boot-env --source empty --part-name=uboot-env --ondisk mmcblk --part-type 0x8301 --fixed-size 512K diff --git a/meta-digi-arm/wic/ccmp1-signed-512MB.wks.in b/meta-digi-arm/wic/ccmp1-signed-512MB.wks.in index cae5964be..31e098e13 100644 --- a/meta-digi-arm/wic/ccmp1-signed-512MB.wks.in +++ b/meta-digi-arm/wic/ccmp1-signed-512MB.wks.in @@ -12,16 +12,16 @@ # # FSBL partitions aka TF-A BL2 -part fsbl1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-512MB-sdcard.stm32_signed" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K --align 17 -part fsbl2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-512MB-sdcard.stm32_signed" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K +part fsbl1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-512MB-${BOOTSCHEME_DEFAULT}-sdcard_Signed.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K --align 17 +part fsbl2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/tf-a-${MACHINE}-512MB-${BOOTSCHEME_DEFAULT}-sdcard_Signed.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K # Metadata partitions part metadata1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=metadata1 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/metadata.bin" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K part metadata2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=metadata2 --sourceparams="file=${DEPLOY_DIR_IMAGE}/arm-trusted-firmware/metadata.bin" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K # Fip partitions -part fip-a --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-a --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-512MB-optee_Signed.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 4fd84c93-54ef-463f-a7ef-ae25ff887087 -part fip-b --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-b --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-512MB-optee_Signed.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 09c54952-d5bf-45af-acee-335303766fb3 +part fip-a --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-a --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-512MB-${BOOTSCHEME_DEFAULT}-sdcard_Signed.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 4fd84c93-54ef-463f-a7ef-ae25ff887087 +part fip-b --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-b --sourceparams="file=${DEPLOY_DIR_IMAGE}/fip/fip-${MACHINE}-512MB-${BOOTSCHEME_DEFAULT}-sdcard_Signed.bin" --ondisk mmcblk --part-type 19d5df83-11b0-457b-be2c-7559c13142a5 --fixed-size 4096K --uuid 09c54952-d5bf-45af-acee-335303766fb3 # U-BOOT env part u-boot-env --source empty --part-name=uboot-env --ondisk mmcblk --part-type 0x8301 --fixed-size 512K From e9af98f586a1b4dbd5ffb319c954cd0dd04d1deb Mon Sep 17 00:00:00 2001 From: Isaac Hermida Date: Fri, 20 Jun 2025 11:11:31 +0200 Subject: [PATCH 128/138] libpam: ccmp13: disable pam_systemd integration to improve login performance On the CCMP13 platform, the inclusion of pam_systemd in the PAM stack causes excessive delays during login -close to 10 seconds- and can even lead to SSH login failures. Since we do not require per-user systemd services (--user), disable the pam_systemd session module to improve login responsiveness. Signed-off-by: Isaac Hermida --- meta-digi-dey/recipes-extended/pam/libpam_%.bbappend | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 meta-digi-dey/recipes-extended/pam/libpam_%.bbappend diff --git a/meta-digi-dey/recipes-extended/pam/libpam_%.bbappend b/meta-digi-dey/recipes-extended/pam/libpam_%.bbappend new file mode 100644 index 000000000..f91352635 --- /dev/null +++ b/meta-digi-dey/recipes-extended/pam/libpam_%.bbappend @@ -0,0 +1,5 @@ +# Copyright (C) 2025, Digi International Inc. + +do_install:append:ccmp13() { + sed -i '/pam_systemd.so/d' ${D}${sysconfdir}/pam.d/common-session +} From f65dc4440e66c182e0fa0fbe7b99629ee38b9d41 Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Fri, 20 Jun 2025 20:50:22 +0200 Subject: [PATCH 129/138] ccmp1: reorder addresses for fitImage binaries Move the address where the fitImage is loaded after the addresses where the binaries in it are decompressed. This way, the fitImage can grow without size restrictions. The memory map now looks like this: 0xC0000000 Start of memory | | (32 MiB) v 0xC2000000 Kernel loadaddr ($loadaddr) | | (32 MiB) v 0xC4000000 DTB/DTBO load address ($fdt_addr) | (4 MiB) v 0xC4400000 Init ram disk ($initrd_addr) | | | (64 MiB) | v 0xC8400000 ZIP/fitImage address ($fit_addr_r) | ~ | v Signed-off-by: Hector Palacios --- meta-digi-arm/conf/machine/include/ccmp1.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/meta-digi-arm/conf/machine/include/ccmp1.inc b/meta-digi-arm/conf/machine/include/ccmp1.inc index f2d8f7166..9467dfce2 100644 --- a/meta-digi-arm/conf/machine/include/ccmp1.inc +++ b/meta-digi-arm/conf/machine/include/ccmp1.inc @@ -22,9 +22,9 @@ FIP_UBOOT_DTB = "u-boot" # Include boot script into the FIT image UBOOT_ENV = "boot" # For FIT image usage -UBOOT_ENTRYPOINT = "0xc4000000" -UBOOT_DTB_LOADADDRESS = "0xc6000000" -UBOOT_DTBO_LOADADDRESS = "0xc6000000" +UBOOT_ENTRYPOINT = "0xc2000000" +UBOOT_DTB_LOADADDRESS = "0xc4000000" +UBOOT_DTBO_LOADADDRESS = "0xc4000000" # Set InitRAMDisk load address to U-boot initrd_addr UBOOT_RD_LOADADDRESS = "0xc4400000" # U-Boot environment offset (within partition) From 2cbfe2ae0d95e042896251dd55ecb2f2cbac9bc2 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Fri, 20 Jun 2025 17:38:26 +0200 Subject: [PATCH 130/138] tf-a-stm32mp: move TF-A symlink creation to do_deploy Moved deploy_symlinks_atf from SYSROOT_PREPROCESS_FUNCS to do_deploy task to ensure symlinks are created correctly even when rebuilding from the shared state after a "bitbake -c clean tf-a-stm32mp". Override do_deploy[sstate-outputdirs] from the original recipe to allow installing both the deploy artifacts (binaries and symlinks) to the package deploy directory. Signed-off-by: Javier Viguera --- .../tf-a-stm32mp_2.10.bbappend | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend index 5e1423811..f73e29b3e 100644 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-bsp/trusted-firmware-a/tf-a-stm32mp_2.10.bbappend @@ -310,8 +310,6 @@ do_compile() { fi } -# This runs after 'tf_a_sysroot_populate()' which populates all -# TF-A artifacts on the image deploy dir. # The purpose of this function is to create symlinks to the files needed # by the uuu installer that are located in subdirectories. deploy_symlinks_atf() { @@ -325,21 +323,24 @@ deploy_symlinks_atf() { tfa_basename=$(echo ${TF_A_BINARIES} | cut -d',' -f${i}) for dt in ${dt_config}; do TF_A_FILENAME="${tfa_basename}-${dt}-${config}${TF_A_ENCRYPT_SUFFIX}${TF_A_SIGN_SUFFIX}.${TF_A_SUFFIX}" - if [ -f "${DEPLOY_DIR_IMAGE}/${TF_A_BASEDIR}/${TF_A_FILENAME}" ]; then - cd "${DEPLOY_DIR_IMAGE}" + if [ -f "${DEPLOYDIR}/${TF_A_BASEDIR}/${TF_A_FILENAME}" ]; then # symlink TF-A - ln -sf "${TF_A_BASEDIR}/${TF_A_FILENAME}" "${DEPLOY_DIR_IMAGE}" + ln -sf "${TF_A_BASEDIR}/${TF_A_FILENAME}" "${DEPLOYDIR}" fi done done # Last value of 'dt' is good for metadata binary, so use that. if [ "${TF_A_ENABLE_METADATA}" = "1" ]; then - if [ -f "${DEPLOY_DIR_IMAGE}/${TF_A_BASEDIR}/${TF_A_METADATA_BINARY}" ]; then - cd "${DEPLOY_DIR_IMAGE}" + if [ -f "${DEPLOYDIR}/${TF_A_BASEDIR}/${TF_A_METADATA_BINARY}" ]; then # symlink metadata - ln -sf "${TF_A_BASEDIR}/${TF_A_METADATA_BINARY}" "${DEPLOY_DIR_IMAGE}/${TF_A_METADATA_NAME}-${MACHINE}.${TF_A_METADATA_SUFFIX}" + ln -sf "${TF_A_BASEDIR}/${TF_A_METADATA_BINARY}" "${DEPLOYDIR}/${TF_A_METADATA_NAME}-${MACHINE}.${TF_A_METADATA_SUFFIX}" fi fi } -SYSROOT_PREPROCESS_FUNCS += "deploy_symlinks_atf" + +do_deploy[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}" +do_deploy() { + export_binaries ${DEPLOYDIR}${FIP_DIR_TFA_BASE} + deploy_symlinks_atf +} From 01ead103b12bd84b311f2a2782a3f19dc9538a50 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Mon, 23 Jun 2025 17:26:45 +0200 Subject: [PATCH 131/138] meta-digi-dey: remove DEY specific y2038 issue fix As of Yocto 5.0, Poky includes an equivalent solution for the Y2038 issue, so drop the redundant code in meta-digi. https://onedigi.atlassian.net/browse/DEL-9681 Signed-off-by: Javier Viguera --- meta-digi-dey/conf/distro/dey.conf | 1 - meta-digi-dey/conf/distro/include/time64.inc | 17 ----------------- 2 files changed, 18 deletions(-) delete mode 100644 meta-digi-dey/conf/distro/include/time64.inc diff --git a/meta-digi-dey/conf/distro/dey.conf b/meta-digi-dey/conf/distro/dey.conf index 825cb5c4a..5e21cf0ef 100644 --- a/meta-digi-dey/conf/distro/dey.conf +++ b/meta-digi-dey/conf/distro/dey.conf @@ -1,5 +1,4 @@ require conf/distro/poky.conf -require include/time64.inc # Discrete version values. YOCTO_MAJOR = "5" diff --git a/meta-digi-dey/conf/distro/include/time64.inc b/meta-digi-dey/conf/distro/include/time64.inc deleted file mode 100644 index eeb4eb3bf..000000000 --- a/meta-digi-dey/conf/distro/include/time64.inc +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (C) 2024, Digi International Inc. - -# Specify global CFLAGS for target to support 64 bit time -GLIBC_64BIT_TIME_FLAGS = " -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64" - -# Only needed for some 32-bit architectures -TARGET_CC_ARCH:append:arm = "${GLIBC_64BIT_TIME_FLAGS}" - -# Do not apply the 64Bit compile flag to the following packages -# to avoid build errors -GLIBC_64BIT_TIME_FLAGS:pn-alsa-lib = "" -GLIBC_64BIT_TIME_FLAGS:pn-glibc = "" -GLIBC_64BIT_TIME_FLAGS:pn-imx-gst1.0-plugin = "" -GLIBC_64BIT_TIME_FLAGS:pn-kbd = "" -GLIBC_64BIT_TIME_FLAGS:pn-pulseaudio = "" -GLIBC_64BIT_TIME_FLAGS:pn-v4l-utils = "" -GLIBC_64BIT_TIME_FLAGS:pn-zlib = "" From 6b40ed52d24fbe2b32928462640574edcf55baf8 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Mon, 23 Jun 2025 17:27:16 +0200 Subject: [PATCH 132/138] meta-digi-dey: backport ALSA and PulseAudio fixes from meta-imx * Y2038: compile pulseaudio and alsa-lib with 64bit time flags * Add patch to fix playback stuck issues on suspend/resume https://onedigi.atlassian.net/browse/DEL-9681 Signed-off-by: Javier Viguera --- ...x-Fix-resume-with-multiple-instances.patch | 64 +++++++++++++++++++ .../alsa/alsa-lib_%.bbappend | 7 ++ .../pulseaudio/pulseaudio_%.bbappend | 4 +- 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/alsa/alsa-lib/0001-pcm-dmix-Fix-resume-with-multiple-instances.patch create mode 100644 meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/alsa/alsa-lib_%.bbappend diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/alsa/alsa-lib/0001-pcm-dmix-Fix-resume-with-multiple-instances.patch b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/alsa/alsa-lib/0001-pcm-dmix-Fix-resume-with-multiple-instances.patch new file mode 100644 index 000000000..9ab62d434 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/alsa/alsa-lib/0001-pcm-dmix-Fix-resume-with-multiple-instances.patch @@ -0,0 +1,64 @@ +From 2adc30e9838daf62312f32a0a93129d64b1668b3 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Fri, 6 Sep 2024 10:05:56 +0200 +Subject: [PATCH] pcm: dmix: Fix resume with multiple instances + +Upstream-Status: Pending + +The fix for PCM dmix suspend/resume checks spcm->info bit of +SND_PCM_INFO_RESUME for applying a workaround for drivers with the +full resume support. This assumed that scpm->info is exposed from the +underlying slave PCM device. + +The above is true for the first opened instance, but for the second +opened instance, it's a copy from the saved data in shmem. And, we +dropped SND_PCM_INFO_RESUME bit there to assure not to expose the full +resume capability to applications. This resulted in the +inconsistencies, and when the second instance is resumed at first, it +misses the snd_pcm_resume() call, hence the driver doesn't react +properly any longer. + +For addressing it, we keep SND_PCM_INFO_RESUME bit in shmptr->s.info +bits as is, while dropping the bit exposed to apps in +snd_pcm_direct_hw_refine() and *_hw_params() callbacks. + +Fixes: 6d1d620eadf3 ("pcm: dmix: resume workaround for buggy driver") +Reported-and-tested-by: Chancel Liu +Closes: https://lore.kernel.org/DB9PR04MB94988752ED7C43B399E0BC00E3942@DB9PR04MB9498.eurprd04.prod.outlook.com +Signed-off-by: Takashi Iwai +--- + src/pcm/pcm_direct.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c +index e53e5923..17e677f6 100644 +--- a/src/pcm/pcm_direct.c ++++ b/src/pcm/pcm_direct.c +@@ -1018,6 +1018,7 @@ int snd_pcm_direct_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) + } + dshare->timer_ticks = hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE)->max / dshare->slave_period_size; + params->info = dshare->shmptr->s.info; ++ params->info &= ~SND_PCM_INFO_RESUME; + #ifdef REFINE_DEBUG + snd_output_puts(log, "DMIX REFINE (end):\n"); + snd_pcm_hw_params_dump(params, log); +@@ -1031,6 +1032,7 @@ int snd_pcm_direct_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) + snd_pcm_direct_t *dmix = pcm->private_data; + + params->info = dmix->shmptr->s.info; ++ params->info &= ~SND_PCM_INFO_RESUME; + params->rate_num = dmix->shmptr->s.rate; + params->rate_den = 1; + params->fifo_size = 0; +@@ -1183,8 +1185,6 @@ static void save_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm) + COPY_SLAVE(buffer_time); + COPY_SLAVE(sample_bits); + COPY_SLAVE(frame_bits); +- +- dmix->shmptr->s.info &= ~SND_PCM_INFO_RESUME; + } + + #undef COPY_SLAVE +-- +2.46.0 + diff --git a/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/alsa/alsa-lib_%.bbappend b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/alsa/alsa-lib_%.bbappend new file mode 100644 index 000000000..b8cb4e151 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/freescale-layer/recipes-multimedia/alsa/alsa-lib_%.bbappend @@ -0,0 +1,7 @@ +# Copyright (C) 2025, Digi International Inc. + +FILESEXTRAPATHS:prepend := "${THISDIR}/alsa-lib:" +# override meta-freescale/recipes-multimedia/alsa/alsa-lib_%.bbappend +GLIBC_64BIT_TIME_FLAGS:arm:imx-nxp-bsp = " -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64" + +SRC_URI:append = " file://0001-pcm-dmix-Fix-resume-with-multiple-instances.patch" diff --git a/meta-digi-dey/recipes-multimedia/pulseaudio/pulseaudio_%.bbappend b/meta-digi-dey/recipes-multimedia/pulseaudio/pulseaudio_%.bbappend index 2f5f01f3c..f44917355 100644 --- a/meta-digi-dey/recipes-multimedia/pulseaudio/pulseaudio_%.bbappend +++ b/meta-digi-dey/recipes-multimedia/pulseaudio/pulseaudio_%.bbappend @@ -1,4 +1,4 @@ -# Copyright (C) 2019-2024, Digi International Inc. +# Copyright (C) 2019-2025, Digi International Inc. FILESEXTRAPATHS:prepend := "${THISDIR}/${BPN}:" @@ -57,6 +57,8 @@ SRC_URI:append:mx9-nxp-bsp = " \ # Disable allow-autospawn-for-root as default PACKAGECONFIG:remove = "autospawn-for-root" +PACKAGECONFIG[oss-output] = "-Doss-output=enabled,-Doss-output=disabled," +GLIBC_64BIT_TIME_FLAGS:pn-pulseaudio = " ${@bb.utils.contains('PACKAGECONFIG', 'oss-output', '', ' -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64', d)}" EXTRA_OECONF:append:ccimx6 = " --disable-memfd" From 9f6e78f036c15f4a647603c5e3b90ba83beb0e6a Mon Sep 17 00:00:00 2001 From: Arturo Buzarra Date: Thu, 26 Jun 2025 09:13:42 +0200 Subject: [PATCH 133/138] ccmp25-dvk: integrate Sony IMX335 MIPI camera in default device tree As the OmniVision OV5640 camera is now deprecated and no longer supported by most vendors, this commit moves its support to a separate Device Tree overlay, allowing it to be used if needed. Instead, the Sony IMX335 MIPI camera is integrated into the default device tree as the default supported camera for the CCMP25-DVK platform. Signed-off-by: Arturo Buzarra --- meta-digi-arm/conf/machine/ccmp25-dvk.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meta-digi-arm/conf/machine/ccmp25-dvk.conf b/meta-digi-arm/conf/machine/ccmp25-dvk.conf index d7ab816b4..df8c187c3 100644 --- a/meta-digi-arm/conf/machine/ccmp25-dvk.conf +++ b/meta-digi-arm/conf/machine/ccmp25-dvk.conf @@ -54,7 +54,7 @@ STM32MP_KERNEL_DEVICETREE:ccmp25-dvk += " \ ccmp25_wifi.dtbo \ ccmp25-dvk_e55rb-i-mw346-c-mipi-dsi.dtbo \ ccmp25-dvk_g101evn010-lvds.dtbo \ - ccmp25-dvk_imx335-mipi-csi.dtbo \ + ccmp25-dvk_ov5640-mipi-csi.dtbo \ ccmp25-dvk_mikroe-accel2-click.dtbo \ ccmp25-dvk_mikroe-gyro-click.dtbo \ ccmp25-dvk_mikroe-i2c-to-spi-click.dtbo \ From 5054410dfa6a35911a0f5d6d33cbbce1d7d40d49 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Wed, 25 Jun 2025 18:34:03 +0200 Subject: [PATCH 134/138] packagegroup-core-x11-sato: reduce footprint for ccimx6ul-based images * Disable GTK-based gstreamer player and connman network manager * Remove matchbox-terminal to avoid pulling ICU * Avoid pulseaudio client configuration to prevent duplicate instances Also, delete the autospawn-for-root package config removal in our bbappend, as that is not enabled by default in the recipe. https://onedigi.atlassian.net/browse/DEL-9685 Signed-off-by: Javier Viguera --- .../pulseaudio/pulseaudio_%.bbappend | 2 -- .../packagegroup-core-x11-sato.bbappend | 24 ++++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/meta-digi-dey/recipes-multimedia/pulseaudio/pulseaudio_%.bbappend b/meta-digi-dey/recipes-multimedia/pulseaudio/pulseaudio_%.bbappend index f44917355..a000cdb95 100644 --- a/meta-digi-dey/recipes-multimedia/pulseaudio/pulseaudio_%.bbappend +++ b/meta-digi-dey/recipes-multimedia/pulseaudio/pulseaudio_%.bbappend @@ -55,8 +55,6 @@ SRC_URI:append:mx9-nxp-bsp = " \ file://default.pa \ " -# Disable allow-autospawn-for-root as default -PACKAGECONFIG:remove = "autospawn-for-root" PACKAGECONFIG[oss-output] = "-Doss-output=enabled,-Doss-output=disabled," GLIBC_64BIT_TIME_FLAGS:pn-pulseaudio = " ${@bb.utils.contains('PACKAGECONFIG', 'oss-output', '', ' -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64', d)}" diff --git a/meta-digi-dey/recipes-sato/packagegroups/packagegroup-core-x11-sato.bbappend b/meta-digi-dey/recipes-sato/packagegroups/packagegroup-core-x11-sato.bbappend index 12a85f215..2aa811ef7 100644 --- a/meta-digi-dey/recipes-sato/packagegroups/packagegroup-core-x11-sato.bbappend +++ b/meta-digi-dey/recipes-sato/packagegroups/packagegroup-core-x11-sato.bbappend @@ -1,15 +1,17 @@ -# Copyright (C) 2014-2021, Digi International Inc. +# Copyright (C) 2014-2025, Digi International Inc. -# Disable network manager -NETWORK_MANAGER = "" +# +# DEY only uses x11-sato based images on the CC6UL, so tweak the +# recipe to fit in the small CC6UL rootfs. +# -RDEPENDS:${PN}-apps:remove:ccimx6ul = "gst-player" +# Disable GTK-based gstreamer examples and network manager +GSTEXAMPLES:dey = "" +NETWORK_MANAGER:dey = "" -matchbox-base = "${@bb.utils.contains('DISTRO_FEATURES', 'x11 wayland', ' \ - matchbox-desktop matchbox-session-sato matchbox-keyboard matchbox-keyboard-applet matchbox-keyboard-im matchbox-config-gtk', '', d)}" -matchbox-apps = "${@bb.utils.contains('DISTRO_FEATURES', 'x11', 'matchbox-terminal', '', d)}" +# Prevent installing ICU package +RDEPENDS:${PN}-apps:remove:dey = "matchbox-terminal" -RDEPENDS:${PN}-base:remove = "${matchbox-base}" -RDEPENDS:${PN}-apps:remove = "${matchbox-apps} \ - gst-player-bin \ -" +# Prevent launching a second instance of pulseaudio as +# DEY uses system-wide pulseaudio +RDEPENDS:${PN}-base:remove:dey = "pulseaudio-client-conf-sato" From 053adc70c02d837b983d38038d13045c202602b8 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Wed, 25 Jun 2025 18:34:33 +0200 Subject: [PATCH 135/138] meta-digi-dey: remove curl bbappend overriding resolver backend Removed curl_%.bbappend which forced the use of ares over the default threaded-resolver. We did this customization for NetworkManager long ago in the context of network failover. Later we dropped it from NM, but the customization in curl remained. This saves approx. 100KB in the rootfs (libcares.so). Signed-off-by: Javier Viguera --- meta-digi-dey/recipes-support/curl/curl_%.bbappend | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 meta-digi-dey/recipes-support/curl/curl_%.bbappend diff --git a/meta-digi-dey/recipes-support/curl/curl_%.bbappend b/meta-digi-dey/recipes-support/curl/curl_%.bbappend deleted file mode 100644 index 563172d5b..000000000 --- a/meta-digi-dey/recipes-support/curl/curl_%.bbappend +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (C) 2017,2018, Digi International Inc. - -# 'ares' and 'threaded-resolver' are mutually exclusive -PACKAGECONFIG:remove:class-target = "threaded-resolver" -PACKAGECONFIG:append:class-target = " ares" From 5d11be4d2b3e9767db9a6ca1b55e912547ff227a Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Mon, 23 Jun 2025 16:30:51 +0200 Subject: [PATCH 136/138] optee-client: change secure storage and TEE log paths The default secure storage (/var/lib/tee) is a tmpfs and not persistent across reboots. Change it to the data partition (/mnt/data/tee) when TrustFence file system encryption enabled For the log file, do use the /var/log/ directory instead of default /data Signed-off-by: Hector Palacios https://onedigi.atlassian.net/browse/DEL-9683 --- .../recipes-security/optee/optee-client_4.0.bb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client_4.0.bb b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client_4.0.bb index 1fde6e3ff..0d12e5d77 100755 --- a/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client_4.0.bb +++ b/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-security/optee/optee-client_4.0.bb @@ -22,11 +22,15 @@ DEPENDS += "util-linux-libuuid" SYSTEMD_SERVICE:${PN} = "tee-supplicant.service" +SECURE_STORAGE_PATH ?= "${@oe.utils.vartrue('TRUSTFENCE_FILE_BASED_ENCRYPT', \ + '-DCFG_TEE_FS_PARENT_PATH=/mnt/data/tee', \ + '${localstatedir}/lib/tee', d)}" + EXTRA_OECMAKE = " \ - -DCFG_TEE_FS_PARENT_PATH='${localstatedir}/lib/tee' \ + -DCFG_TEE_FS_PARENT_PATH='${SECURE_STORAGE_PATH}' \ -DCFG_WERROR=OFF \ -DCFG_TEE_CLIENT_LOG_LEVEL=2 \ - -DCFG_TEE_CLIENT_LOG_FILE='/data/tee/teec.log' \ + -DCFG_TEE_CLIENT_LOG_FILE='${localstatedir}/log/tee/teec.log' \ -DBUILD_SHARED_LIBS=ON \ -DRPMB_EMU=0 \ " @@ -42,7 +46,7 @@ do_install:append() { fi install -d ${D}${sysconfdir}/udev/rules.d install -m 0644 ${WORKDIR}/optee-udev.rules ${D}${sysconfdir}/udev/rules.d/optee.rules - install -d -m770 -o root -g tee ${D}${localstatedir}/lib/tee + install -d -m770 -o root -g tee ${D}${SECURE_STORAGE_PATH} } FILES:${PN} += "${sysconfdir} ${localstatedir}" From d3e6f8648bc03ac75c43dc8a6f9b3888e8e73ce7 Mon Sep 17 00:00:00 2001 From: Francisco Gil Date: Tue, 1 Jul 2025 13:27:36 +0200 Subject: [PATCH 137/138] cc6ul: vsftpd-cert: increase init priority to ensure certificates generation The vsftpd-cert init script was previously starting too late, causing vsftpd to start before the certificates were generated. The priority has been increased (to 70) so that vsftpd-cert runs earlier during boot. Signed-off-by: Francisco Gil --- meta-digi-dey/recipes-connectivity/vsftpd/vsftpd_%.bbappend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd_%.bbappend b/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd_%.bbappend index b14c7b09d..a66772ae3 100644 --- a/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd_%.bbappend +++ b/meta-digi-dey/recipes-connectivity/vsftpd/vsftpd_%.bbappend @@ -77,7 +77,7 @@ FILES:${PN}-cert = " \ INITSCRIPT_PACKAGES += "${@bb.utils.contains('PACKAGECONFIG', 'openssl', '${PN}-cert', '', d)}" INITSCRIPT_NAME:${PN}-cert = "vsftpd-cert" -INITSCRIPT_PARAMS:${PN}-cert = "start 99 3 5 . stop 20 0 1 2 6 ." +INITSCRIPT_PARAMS:${PN}-cert = "start 70 3 5 . stop 20 0 1 2 6 ." SYSTEMD_PACKAGES += "${@bb.utils.contains('PACKAGECONFIG', 'openssl', '${PN}-cert', '', d)}" SYSTEMD_SERVICE:${PN}-cert = "vsftpd-cert.service" From 35148654c733c5f52b123e57b33e999f55607e96 Mon Sep 17 00:00:00 2001 From: Javier Viguera Date: Tue, 1 Jul 2025 13:40:00 +0200 Subject: [PATCH 138/138] u-boot: drop '-dtb' from signed/encrypted image filenames Signed-off-by: Javier Viguera --- .../classes/image_types_digi.bbclass | 2 +- .../recipes-bsp/u-boot/u-boot-dey.inc | 54 ++++++++----------- .../classes/dey-swupdate-common.bbclass | 2 - 3 files changed, 23 insertions(+), 35 deletions(-) diff --git a/meta-digi-arm/classes/image_types_digi.bbclass b/meta-digi-arm/classes/image_types_digi.bbclass index 3bcbbcdfb..64cdc6d18 100644 --- a/meta-digi-arm/classes/image_types_digi.bbclass +++ b/meta-digi-arm/classes/image_types_digi.bbclass @@ -331,7 +331,7 @@ IMAGE_CMD:sdcard() { else if [ "${TRUSTFENCE_SIGN}" = "1" ]; then if [ "${BOOTLOADER_IMAGE_RECIPE}" = "u-boot" ]; then - SDIMG_BOOT="$(readlink -e ${SDIMG_BOOTLOADER} | sed -e 's,u-boot-,u-boot-dtb-signed-,g')" + SDIMG_BOOT="$(readlink -e ${SDIMG_BOOTLOADER} | sed -e 's,u-boot-,u-boot-signed-,g')" else SDIMG_BOOT="$(readlink -e ${SDIMG_BOOTLOADER} | sed -e 's,imx-boot-,imx-boot-signed-,g')" fi diff --git a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc index fd75cc713..9276797af 100644 --- a/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc +++ b/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey.inc @@ -166,38 +166,28 @@ adapt_uboot_filenames() { } sign_uboot() { - # This function only applies to CC6, CC6Plus and CC6UL - if [ -n "${UBOOT_CONFIG}" ] - then - for config in ${UBOOT_MACHINE}; do - i=$(expr $i + 1); - for type in ${UBOOT_CONFIG}; do - j=$(expr $j + 1); - if [ $j -eq $i ] - then - cd ${DEPLOYDIR} - if [ "${BOOTLOADER_IMAGE_RECIPE}" = "u-boot" ]; then - install ${B}/${config}/SRK_efuses.bin SRK_efuses-${PV}-${PR}.bin - ln -sf SRK_efuses-${PV}-${PR}.bin SRK_efuses.bin - - install ${B}/${config}/${UBOOT_BINARYNAME}-dtb-signed.imx ${UBOOT_BINARYNAME}-dtb-signed-${type}-${PV}-${PR}.${UBOOT_SUFFIX} - ln -sf ${UBOOT_BINARYNAME}-dtb-signed-${type}-${PV}-${PR}.${UBOOT_SUFFIX} ${UBOOT_BINARYNAME}-dtb-signed-${type}.${UBOOT_SUFFIX} - - install ${B}/${config}/${UBOOT_BINARYNAME}-dtb-usb-signed.imx ${UBOOT_BINARYNAME}-dtb-usb-signed-${type}-${PV}-${PR}.${UBOOT_SUFFIX} - ln -sf ${UBOOT_BINARYNAME}-dtb-usb-signed-${type}-${PV}-${PR}.${UBOOT_SUFFIX} ${UBOOT_BINARYNAME}-dtb-usb-signed-${type}.${UBOOT_SUFFIX} - - if [ "${TRUSTFENCE_DEK_PATH}" != "0" ] - then - install ${B}/${config}/${UBOOT_BINARYNAME}-dtb-encrypted.imx ${UBOOT_BINARYNAME}-dtb-encrypted-${type}-${PV}-${PR}.${UBOOT_SUFFIX} - ln -sf ${UBOOT_BINARYNAME}-dtb-encrypted-${type}-${PV}-${PR}.${UBOOT_SUFFIX} ${UBOOT_BINARYNAME}-dtb-encrypted-${type}.${UBOOT_SUFFIX} - fi - fi - fi - done - unset j - done - unset i - fi + # This function only applies to CC6, CC6Plus and CC6UL + [ -z "${UBOOT_CONFIG}" ] && return + for config in ${UBOOT_MACHINE}; do + i=$(expr $i + 1) + for type in ${UBOOT_CONFIG}; do + j=$(expr $j + 1) + if [ $j -eq $i ]; then + cd ${DEPLOYDIR} + cp -fp ${B}/${config}/SRK_efuses.bin SRK_efuses-${PV}-${PR}.bin + ln -sf SRK_efuses-${PV}-${PR}.bin SRK_efuses.bin + cp -fp ${B}/${config}/${UBOOT_BINARYNAME}-dtb-signed.imx ${UBOOT_BINARYNAME}-signed-${type}-${PV}-${PR}.${UBOOT_SUFFIX} + ln -sf ${UBOOT_BINARYNAME}-signed-${type}-${PV}-${PR}.${UBOOT_SUFFIX} ${UBOOT_BINARYNAME}-signed-${type}.${UBOOT_SUFFIX} + cp -fp ${B}/${config}/${UBOOT_BINARYNAME}-dtb-usb-signed.imx ${UBOOT_BINARYNAME}-usb-signed-${type}-${PV}-${PR}.${UBOOT_SUFFIX} + ln -sf ${UBOOT_BINARYNAME}-usb-signed-${type}-${PV}-${PR}.${UBOOT_SUFFIX} ${UBOOT_BINARYNAME}-usb-signed-${type}.${UBOOT_SUFFIX} + if [ "${TRUSTFENCE_DEK_PATH}" != "0" ]; then + cp -fp ${B}/${config}/${UBOOT_BINARYNAME}-dtb-encrypted.imx ${UBOOT_BINARYNAME}-encrypted-${type}-${PV}-${PR}.${UBOOT_SUFFIX} + ln -sf ${UBOOT_BINARYNAME}-encrypted-${type}-${PV}-${PR}.${UBOOT_SUFFIX} ${UBOOT_BINARYNAME}-encrypted-${type}.${UBOOT_SUFFIX} + fi + fi + done + unset j + done } # ----------------------------------------------------------------------------- diff --git a/meta-digi-dey/classes/dey-swupdate-common.bbclass b/meta-digi-dey/classes/dey-swupdate-common.bbclass index cdcc0af2a..671900fbd 100644 --- a/meta-digi-dey/classes/dey-swupdate-common.bbclass +++ b/meta-digi-dey/classes/dey-swupdate-common.bbclass @@ -100,8 +100,6 @@ SWUPDATE_UBOOT_SCRIPT_NAME = "${@os.path.basename(d.getVar('SWUPDATE_UBOOT_SCRIP def get_uboot_prefix(d): prefix = d.getVar('UBOOT_PREFIX') if d.getVar('DEY_SOC_VENDOR') == "NXP" and d.getVar('TRUSTFENCE_ENABLED') == "1": - if "ccimx6" in d.getVar('MACHINE'): - prefix = f"{prefix}-dtb" if d.getVar('TRUSTFENCE_DEK_PATH') and d.getVar('TRUSTFENCE_DEK_PATH') != "0": prefix = f"{prefix}-encrypted" else:

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 literal 0 HcmV?d00001 diff --git a/meta-digi-dey/recipes-digi/nxp-wlan-utils/files/nanapp b/meta-digi-dey/recipes-digi/nxp-wlan-utils/files/nanapp new file mode 100755 index 0000000000000000000000000000000000000000..a19c137d56b53399ce32bfb81402c1124d346a99 GIT binary patch literal 1538416 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