Merge Official Source

Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
This commit is contained in:
Tianling Shen
2026-05-16 16:36:38 +08:00
54 changed files with 3593 additions and 1189 deletions
+2 -2
View File
@@ -1,2 +1,2 @@
LINUX_VERSION-6.6 = .137
LINUX_KERNEL_HASH-6.6.137 = 92f1e90d8be49f9d7f989e563c97a4f39cd7731025d7b81152814c68b5bd56c4
LINUX_VERSION-6.6 = .139
LINUX_KERNEL_HASH-6.6.139 = a7cd71371dae38ac94c6c332fe16391b83fe173158020e2ea1b37277f8f3d740
+3 -3
View File
@@ -8,13 +8,13 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=mbedtls
PKG_VERSION:=3.6.5
PKG_RELEASE:=1
PKG_VERSION:=3.6.6
PKG_RELEASE:=2
PKG_BUILD_FLAGS:=no-mips16 gc-sections no-lto
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
PKG_SOURCE_URL=https://github.com/Mbed-TLS/$(PKG_NAME)/releases/download/$(PKG_NAME)-$(PKG_VERSION)
PKG_HASH:=4a11f1777bb95bf4ad96721cac945a26e04bf19f57d905f241fe77ebeddf46d8
PKG_HASH:=8fb65fae8dcae5840f793c0a334860a411f884cc537ea290ce1c52bb64ca007a
PKG_LICENSE:=GPL-2.0-or-later
PKG_LICENSE_FILES:=LICENSE
@@ -0,0 +1,84 @@
From c4738fab064f018aa251577fae9399040a86bc33 Mon Sep 17 00:00:00 2001
From: Viktor Sokolovskiy <maokaman@gmail.com>
Date: Sat, 4 Apr 2026 03:57:04 +0300
Subject: [PATCH] ssl: accept TLS 1.2 rsa_pss_rsae signature schemes
Signed-off-by: Viktor Sokolovskiy <maokaman@gmail.com>
(cherry picked from commit c064ba0edb86e44f3c1c8dcaf05b1139407eebd4)
---
ChangeLog.d/fix-tls12-rsa-pss-sigalgs.txt | 3 +++
library/ssl_misc.h | 19 +++++++++++++++++++
tests/suites/test_suite_ssl.data | 12 ++++++++++++
tests/suites/test_suite_ssl.function | 8 ++++++++
4 files changed, 42 insertions(+)
create mode 100644 ChangeLog.d/fix-tls12-rsa-pss-sigalgs.txt
--- /dev/null
+++ b/ChangeLog.d/fix-tls12-rsa-pss-sigalgs.txt
@@ -0,0 +1,3 @@
+Bugfix
+ * Fix a TLS 1.2 regression that caused clients to reject valid
+ ServerKeyExchange signatures using RSA-PSS signature scheme values.
--- a/library/ssl_misc.h
+++ b/library/ssl_misc.h
@@ -2591,6 +2591,25 @@ static inline int mbedtls_ssl_get_pk_typ
static inline int mbedtls_ssl_tls12_sig_alg_is_supported(
const uint16_t sig_alg)
{
+#if defined(PSA_WANT_ALG_RSA_PSS)
+ switch (sig_alg) {
+#if defined(PSA_WANT_ALG_SHA_256)
+ case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256:
+ return 1;
+#endif
+#if defined(PSA_WANT_ALG_SHA_384)
+ case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA384:
+ return 1;
+#endif
+#if defined(PSA_WANT_ALG_SHA_512)
+ case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA512:
+ return 1;
+#endif
+ default:
+ break;
+ }
+#endif /* PSA_WANT_ALG_RSA_PSS */
+
/* High byte is hash */
unsigned char hash = MBEDTLS_BYTE_1(sig_alg);
unsigned char sig = MBEDTLS_BYTE_0(sig_alg);
--- a/tests/suites/test_suite_ssl.data
+++ b/tests/suites/test_suite_ssl.data
@@ -3546,3 +3546,15 @@ send_invalid_sig_alg:MBEDTLS_SSL_SIG_ECD
Default verify_result before doing a handshake
verify_result_without_handshake
+
+TLS 1.2 accepts rsa_pss_rsae_sha256 in signature_algorithm
+depends_on:PSA_WANT_ALG_RSA_PSS:PSA_WANT_ALG_SHA_256
+ssl_tls12_sig_alg_supported:MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256:1
+
+TLS 1.2 accepts rsa_pss_rsae_sha384 in signature_algorithm
+depends_on:PSA_WANT_ALG_RSA_PSS:PSA_WANT_ALG_SHA_384
+ssl_tls12_sig_alg_supported:MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA384:1
+
+TLS 1.2 accepts rsa_pss_rsae_sha512 in signature_algorithm
+depends_on:PSA_WANT_ALG_RSA_PSS:PSA_WANT_ALG_SHA_512
+ssl_tls12_sig_alg_supported:MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA512:1
--- a/tests/suites/test_suite_ssl.function
+++ b/tests/suites/test_suite_ssl.function
@@ -5861,6 +5861,14 @@ exit:
}
/* END_CASE */
+/* BEGIN_CASE depends_on:MBEDTLS_SSL_PROTO_TLS1_2:MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */
+void ssl_tls12_sig_alg_supported(int sig_alg, int expected)
+{
+ TEST_EQUAL(mbedtls_ssl_tls12_sig_alg_is_supported((uint16_t) sig_alg),
+ expected);
+}
+/* END_CASE */
+
/* BEGIN_CASE depends_on:MBEDTLS_SSL_KEYING_MATERIAL_EXPORT:MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED:PSA_WANT_ECC_SECP_R1_384:PSA_WANT_ALG_SHA_256 */
void ssl_tls_exporter_consistent_result(int proto, int exported_key_length, int use_context)
{
@@ -0,0 +1,236 @@
From 3833db7c7cb167e8eda2ec5e07da70cd834e88d4 Mon Sep 17 00:00:00 2001
From: Viktor Sokolovskiy <maokaman@gmail.com>
Date: Fri, 17 Apr 2026 18:52:34 +0300
Subject: [PATCH] ssl: narrow TLS 1.2 RSA-PSS handling and add interop coverage
Narrow TLS 1.2 RSA-PSS handling to the client ServerKeyExchange parse path and add OpenSSL and GnuTLS interoperability tests.
Signed-off-by: Viktor Sokolovskiy <maokaman@gmail.com>
---
ChangeLog.d/fix-tls12-rsa-pss-sigalgs.txt | 3 +-
library/ssl_misc.h | 19 -------
library/ssl_tls12_client.c | 63 +++++++++++++++++++----
tests/ssl-opt.sh | 38 +++++++++++++-
tests/suites/test_suite_ssl.data | 12 -----
tests/suites/test_suite_ssl.function | 8 ---
6 files changed, 91 insertions(+), 52 deletions(-)
--- a/ChangeLog.d/fix-tls12-rsa-pss-sigalgs.txt
+++ b/ChangeLog.d/fix-tls12-rsa-pss-sigalgs.txt
@@ -1,3 +1,4 @@
Bugfix
* Fix a TLS 1.2 regression that caused clients to reject valid
- ServerKeyExchange signatures using RSA-PSS signature scheme values.
+ ServerKeyExchange signatures using RSA-PSS signature algorithms.
+ Fixes #10668.
--- a/library/ssl_misc.h
+++ b/library/ssl_misc.h
@@ -2591,25 +2591,6 @@ static inline int mbedtls_ssl_get_pk_typ
static inline int mbedtls_ssl_tls12_sig_alg_is_supported(
const uint16_t sig_alg)
{
-#if defined(PSA_WANT_ALG_RSA_PSS)
- switch (sig_alg) {
-#if defined(PSA_WANT_ALG_SHA_256)
- case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256:
- return 1;
-#endif
-#if defined(PSA_WANT_ALG_SHA_384)
- case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA384:
- return 1;
-#endif
-#if defined(PSA_WANT_ALG_SHA_512)
- case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA512:
- return 1;
-#endif
- default:
- break;
- }
-#endif /* PSA_WANT_ALG_RSA_PSS */
-
/* High byte is hash */
unsigned char hash = MBEDTLS_BYTE_1(sig_alg);
unsigned char sig = MBEDTLS_BYTE_0(sig_alg);
--- a/library/ssl_tls12_client.c
+++ b/library/ssl_tls12_client.c
@@ -13,6 +13,7 @@
#include "mbedtls/ssl.h"
#include "ssl_client.h"
+#include "ssl_debug_helpers.h"
#include "ssl_misc.h"
#include "debug_internal.h"
#include "mbedtls/error.h"
@@ -2087,32 +2088,72 @@ static int ssl_parse_signature_algorithm
{
if (mbedtls_ssl_get_pk_type_and_md_alg_from_sig_alg(sig_alg, pk_alg, md_alg) != 0) {
MBEDTLS_SSL_DEBUG_MSG(1,
- ("Server used unsupported value in SigAlg extension 0x%04x",
- sig_alg));
+ ("Server used unsupported %s signature algorithm",
+ mbedtls_ssl_sig_alg_to_str(sig_alg)));
return MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER;
}
/*
- * mbedtls_ssl_get_pk_sigalg_and_md_alg_from_sig_alg() understands sig_alg code points across
- * TLS versions. Make sure that the received sig_alg extension is valid in TLS 1.2.
+ * mbedtls_ssl_get_pk_type_and_md_alg_from_sig_alg() understands
+ * signature algorithm code points from both TLS 1.2 and TLS 1.3. Make sure
+ * that the selected signature algorithm is acceptable when TLS 1.2 is
+ * negotiated.
+ *
+ * In TLS 1.2, RSA-PSS signature algorithms (rsa_pss_rsae_*) are not
+ * defined by RFC 5246. However, RFC 8446 Section 4.2.3 requires that
+ * implementations which advertise support for RSASSA-PSS must be
+ * prepared to accept such signatures even when TLS 1.2 is negotiated,
+ * provided they were offered in the signature_algorithms extension.
+ *
+ * Therefore, we allow rsa_pss_rsae_* here if:
+ * - the implementation supports them, and
+ * - they were offered in the signature_algorithms extension (checked by
+ * `mbedtls_ssl_sig_alg_is_offered()` below).
+ *
+ * If we were to add full support for rsa_pss_rsae_* signature algorithms
+ * in TLS 1.2, we should then integrate RSA-PSS into the TLS 1.2 signature
+ * algorithm support logic (`mbedtls_ssl_tls12_sig_alg_is_supported()`)
+ * instead of handling it as a special case here.
*/
if (!mbedtls_ssl_sig_alg_is_supported(ssl, sig_alg)) {
- MBEDTLS_SSL_DEBUG_MSG(1,
- ("Server used unsupported value in SigAlg extension 0x%04x",
- sig_alg));
- return MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER;
+ switch (sig_alg) {
+#if defined(PSA_WANT_ALG_RSA_PSS)
+#if defined(PSA_WANT_ALG_SHA_256)
+ case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256:
+#endif
+#if defined(PSA_WANT_ALG_SHA_384)
+ case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA384:
+#endif
+#if defined(PSA_WANT_ALG_SHA_512)
+ case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA512:
+#endif
+#if defined(PSA_WANT_ALG_SHA_256) || defined(PSA_WANT_ALG_SHA_384) || defined(PSA_WANT_ALG_SHA_512)
+ MBEDTLS_SSL_DEBUG_MSG(3,
+ ("Accepting TLS 1.2 RSA-PSS signature algorithm %s via compatibility exception",
+ mbedtls_ssl_sig_alg_to_str(sig_alg)));
+ break;
+#endif
+#endif /* PSA_WANT_ALG_RSA_PSS */
+ default:
+ MBEDTLS_SSL_DEBUG_MSG(1,
+ ("Server used unsupported %s signature algorithm",
+ mbedtls_ssl_sig_alg_to_str(sig_alg)));
+ return MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER;
+ }
}
/*
* Check if the signature algorithm is acceptable
*/
if (!mbedtls_ssl_sig_alg_is_offered(ssl, sig_alg)) {
- MBEDTLS_SSL_DEBUG_MSG(1, ("Server used SigAlg value 0x%04x that was not offered", sig_alg));
+ MBEDTLS_SSL_DEBUG_MSG(1,
+ ("Server used the signature algorithm %s that was not offered",
+ mbedtls_ssl_sig_alg_to_str(sig_alg)));
return MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER;
}
- MBEDTLS_SSL_DEBUG_MSG(2, ("Server used SignatureAlgorithm %d", sig_alg & 0x00FF));
- MBEDTLS_SSL_DEBUG_MSG(2, ("Server used HashAlgorithm %d", sig_alg >> 8));
+ MBEDTLS_SSL_DEBUG_MSG(2, ("Server used the signature algorithm %s",
+ mbedtls_ssl_sig_alg_to_str(sig_alg)));
return 0;
}
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -14948,7 +14948,6 @@ run_test "TLS 1.2: Check rsa_pss_rsae
-c "Protocol is TLSv1.2" \
-c "HTTP/1.0 200 [Oo][Kk]"
-
requires_gnutls_tls1_3
requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
requires_config_enabled MBEDTLS_DEBUG_C
@@ -14964,6 +14963,43 @@ run_test "TLS 1.2: Check rsa_pss_rsae
-c "Protocol is TLSv1.2" \
-c "HTTP/1.0 200 [Oo][Kk]"
+requires_openssl_tls1_3_with_compatible_ephemeral
+requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
+requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3
+requires_config_enabled MBEDTLS_DEBUG_C
+requires_config_enabled MBEDTLS_SSL_CLI_C
+requires_config_enabled PSA_WANT_ALG_RSA_PSS
+requires_config_enabled PSA_WANT_ALG_SHA_256
+run_test "TLS 1.2: Server forces TLS 1.2 and rsa_pss_rsae_sha256, m->O" \
+ "$O_NEXT_SRV_NO_CERT -cert $DATA_FILES_PATH/server2-sha256.crt -key $DATA_FILES_PATH/server2.key
+ -tls1_2 -sigalgs rsa_pss_rsae_sha256 " \
+ "$P_CLI debug_level=3" \
+ 0 \
+ -c "sent signature scheme \\[804\\] rsa_pss_rsae_sha256" \
+ -c "Perform .* computation of digest of ServerKeyExchange" \
+ -c "Server used the signature algorithm rsa_pss_rsae_sha256" \
+ -c "Protocol is TLSv1.2" \
+ -c "HTTP/1.0 200 [Oo][Kk]"
+
+requires_gnutls_tls1_3
+requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
+requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3
+requires_config_enabled MBEDTLS_DEBUG_C
+requires_config_enabled MBEDTLS_SSL_CLI_C
+requires_config_enabled PSA_WANT_ALG_RSA_PSS
+requires_config_enabled PSA_WANT_ALG_SHA_256
+run_test "TLS 1.2: Server forces TLS 1.2 and rsa_pss_rsae_sha256, m->G" \
+ "$G_NEXT_SRV_NO_CERT --x509certfile $DATA_FILES_PATH/server2-sha256.crt --x509keyfile $DATA_FILES_PATH/server2.key
+ --disable-client-cert
+ --priority=NORMAL:-VERS-ALL:+VERS-TLS1.2:-SIGN-ALL:+SIGN-RSA-PSS-RSAE-SHA256" \
+ "$P_CLI debug_level=3" \
+ 0 \
+ -c "sent signature scheme \\[804\\] rsa_pss_rsae_sha256" \
+ -c "Perform .* computation of digest of ServerKeyExchange" \
+ -c "Server used the signature algorithm rsa_pss_rsae_sha256" \
+ -c "Protocol is TLSv1.2" \
+ -c "HTTP/1.0 200 [Oo][Kk]"
+
requires_config_enabled MBEDTLS_SSL_SRV_C
requires_config_enabled MBEDTLS_DEBUG_C
requires_config_enabled MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED
--- a/tests/suites/test_suite_ssl.data
+++ b/tests/suites/test_suite_ssl.data
@@ -3546,15 +3546,3 @@ send_invalid_sig_alg:MBEDTLS_SSL_SIG_ECD
Default verify_result before doing a handshake
verify_result_without_handshake
-
-TLS 1.2 accepts rsa_pss_rsae_sha256 in signature_algorithm
-depends_on:PSA_WANT_ALG_RSA_PSS:PSA_WANT_ALG_SHA_256
-ssl_tls12_sig_alg_supported:MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256:1
-
-TLS 1.2 accepts rsa_pss_rsae_sha384 in signature_algorithm
-depends_on:PSA_WANT_ALG_RSA_PSS:PSA_WANT_ALG_SHA_384
-ssl_tls12_sig_alg_supported:MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA384:1
-
-TLS 1.2 accepts rsa_pss_rsae_sha512 in signature_algorithm
-depends_on:PSA_WANT_ALG_RSA_PSS:PSA_WANT_ALG_SHA_512
-ssl_tls12_sig_alg_supported:MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA512:1
--- a/tests/suites/test_suite_ssl.function
+++ b/tests/suites/test_suite_ssl.function
@@ -5861,14 +5861,6 @@ exit:
}
/* END_CASE */
-/* BEGIN_CASE depends_on:MBEDTLS_SSL_PROTO_TLS1_2:MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */
-void ssl_tls12_sig_alg_supported(int sig_alg, int expected)
-{
- TEST_EQUAL(mbedtls_ssl_tls12_sig_alg_is_supported((uint16_t) sig_alg),
- expected);
-}
-/* END_CASE */
-
/* BEGIN_CASE depends_on:MBEDTLS_SSL_KEYING_MATERIAL_EXPORT:MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED:PSA_WANT_ECC_SECP_R1_384:PSA_WANT_ALG_SHA_256 */
void ssl_tls_exporter_consistent_result(int proto, int exported_key_length, int use_context)
{
@@ -1,6 +1,6 @@
--- a/library/common.h
+++ b/library/common.h
@@ -199,7 +199,7 @@ static inline void mbedtls_xor(unsigned
@@ -224,7 +224,7 @@ static inline void mbedtls_xor(unsigned
uint8x16_t x = veorq_u8(v1, v2);
vst1q_u8(r + i, x);
}
+2 -2
View File
@@ -8,7 +8,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=openssl
PKG_VERSION:=3.0.19
PKG_VERSION:=3.0.20
PKG_RELEASE:=1
PKG_BUILD_FLAGS:=no-mips16 gc-sections no-lto
@@ -21,7 +21,7 @@ PKG_SOURCE_URL:= \
https://www.openssl.org/source/old/$(PKG_BASE)/ \
https://github.com/openssl/openssl/releases/download/$(PKG_NAME)-$(PKG_VERSION)/
PKG_HASH:=fa5a4143b8aae18be53ef2f3caf29a2e0747430b8bc74d32d88335b94ab63072
PKG_HASH:=c80a01dfc70ece4dc21168932c37739042d404d46ccc81a5986dd75314ecda6f
PKG_LICENSE:=Apache-2.0
PKG_LICENSE_FILES:=LICENSE.txt
+3 -3
View File
@@ -8,13 +8,13 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=wolfssl
PKG_VERSION:=5.7.6
PKG_VERSION:=5.9.1
PKG_REAL_VERSION:=$(PKG_VERSION)-stable
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_REAL_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/wolfSSL/wolfssl/archive/v$(PKG_REAL_VERSION)
PKG_HASH:=52b1e439e30d1ed8162a16308a8525a862183b67aa30373b11166ecbab000d63
PKG_HASH:=d5ca7af48cd2d9a91d539e9baedeba55a0605a28d7ac8b01dc3d5254a13ca341
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(BUILD_VARIANT)/$(PKG_NAME)-$(PKG_REAL_VERSION)
@@ -22,7 +22,7 @@ PKG_FIXUP:=libtool libtool-abiver
PKG_INSTALL:=1
PKG_BUILD_FLAGS:=no-mips16 lto
PKG_BUILD_PARALLEL:=1
PKG_LICENSE:=GPL-2.0-or-later
PKG_LICENSE:=GPL-3.0-or-later
PKG_LICENSE_FILES:=LICENSING COPYING
PKG_MAINTAINER:=Eneas U de Queiroz <cotequeiroz@gmail.com>
PKG_CPE_ID:=cpe:/a:wolfssl:wolfssl
@@ -1,6 +1,6 @@
--- a/wolfssl/wolfcrypt/settings.h
+++ b/wolfssl/wolfcrypt/settings.h
@@ -3722,7 +3722,7 @@ extern void uITRON4_free(void *p) ;
@@ -4154,7 +4154,7 @@ extern void uITRON4_free(void *p) ;
/* warning for not using harden build options (default with ./configure) */
/* do not warn if big integer support is disabled */
@@ -0,0 +1,29 @@
commit ec2fbfbbdaa7d7db1c707dce26ce1a37cfe09660
Author: Simon Kelley <simon@thekelleys.org.uk>
Date: Fri Apr 10 16:29:31 2026 +0100
Fix buffer overflow in struct bigname. CVE-2026-2291
All buffers capable of holding a domain name should be
at least MAXDNAME*2 + 1 bytes long, where MAXDNAME is the maximum
size of a domain name. The accounts for the trailing zero and the
fact that some characters are escaped in the internal representation
of a domain name in dnsmasq.
The declaration of struct bigname get this wrong, with the effect
that a remote attacker capable of asking DNS queries or answering DNS
queries can cause a large OOB write in the heap.
This was first spotted by Andrew S. Fasano.
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -467,7 +467,7 @@ struct interface_name {
};
union bigname {
- char name[MAXDNAME];
+ char name[(2*MAXDNAME) + 1];
union bigname *next; /* freelist */
};
@@ -0,0 +1,63 @@
commit de76f21e115c451cf0653790fc4b209cd4778a07
Author: Simon Kelley <simon@thekelleys.org.uk>
Date: Fri Apr 10 22:16:45 2026 +0100
Fix NSEC bitmap parsing infinite loop. CVE-2026-4890
Report from Royce M <royce@xchglabs.com>.
Location: dnssec.c:1290-1306, dnssec.c:1450-1463
The bitmap window iteration advances by p[1] instead of p[1]+2 (missing the 2-byte window header). With bitmap_length=0, both rdlen and p are
unchanged, causing an infinite loop and dnsmasq stops responding to all queries.
The same code accesses p[2] after only checking rdlen >= 2 without verifying p[1] >= 1, causing OOB reads at 6 locations.
Both bugs are reachable before RRSIG validation (confirmed by the source comment at line 2125), so no valid DNSSEC signatures are needed.
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -1270,10 +1270,10 @@ static int prove_non_existence_nsec(stru
packet checked to be as long as rdlen implies in prove_non_existence() */
/* If we can prove that there's no NS record, return that information. */
- if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) != 0)
+ if (nons && rdlen >= 2 && p[0] == 0 && p[1] >= 1 && (p[2] & (0x80 >> T_NS)) != 0)
*nons = 0;
- if (rdlen >= 2 && p[0] == 0)
+ if (rdlen >= 2 && p[0] == 0 && p[1] >= 1)
{
/* A CNAME answer would also be valid, so if there's a CNAME is should
have been returned. */
@@ -1301,8 +1301,8 @@ static int prove_non_existence_nsec(stru
break; /* finished checking */
}
- rdlen -= p[1];
- p += p[1];
+ rdlen -= p[1] + 2;
+ p += p[1] + 2;
}
return 0;
@@ -1429,7 +1429,7 @@ static int check_nsec3_coverage(struct d
p += hash_len; /* skip next-domain hash */
rdlen -= p - psave;
- if (rdlen >= 2 && p[0] == 0)
+ if (rdlen >= 2 && p[0] == 0 && p[1] >= 1)
{
/* If we can prove that there's no NS record, return that information. */
if (nons && (p[2] & (0x80 >> T_NS)) != 0)
@@ -1458,8 +1458,8 @@ static int check_nsec3_coverage(struct d
break; /* finished checking */
}
- rdlen -= p[1];
- p += p[1];
+ rdlen -= p[1] + 2;
+ p += p[1] + 2;
}
return 1;
@@ -0,0 +1,32 @@
commit 2cacea42e4d45717bd0ce3ccfe8e78960245e5da
Author: Simon Kelley <simon@thekelleys.org.uk>
Date: Wed Mar 25 23:04:08 2026 +0000
Verify rdlen field in RRSIG packets. CVE-2026-4891
Bug report from Royce M <royce@xchglabs.com>
This avoids crafted packets which give a value for rdlen _less_
then the space taken up by the fixed data and the signer's name
and engender a negative calculated length for the signature.
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -546,10 +546,14 @@ static int validate_rrset(time_t now, st
*ttl_out = ttl;
}
-
+
+ /* Don't trust rdlen not to be too small and give us a negative sig_len
+ It has already been checked that it doesn't run us off the end
+ of the packet. */
+ if ((sig_len = rdlen - (p - psav)) <= 0)
+ return STAT_BOGUS;
+
sig = p;
- sig_len = rdlen - (p - psav);
-
nsigttl = htonl(orig_ttl);
hash->update(ctx, 18, psav);
@@ -0,0 +1,28 @@
commit 011a36c51438c986535a7248ed2e7f424f8e1078
Author: Simon Kelley <simon@thekelleys.org.uk>
Date: Wed Mar 25 23:16:35 2026 +0000
Fix buffer overflow in helper.c with large CLIDs. CVE-2026-4892
Bug reported bt Royce M <royce@xchglabs.com>
Location: helper.c:265-270
DHCPv6 CLIDs can be up to 65535 bytes. When --dhcp-script is configured,
the helper hex-encodes raw CLID bytes via sprintf("%.2x") into daemon->packet (5131 bytes).
A 1000-byte CLID writes ~3000 bytes. The helper process retains root privileges.
Note: log6_packet() correctly caps CLID to 100 bytes for logging, but the helper code path was missed.
--- a/src/helper.c
+++ b/src/helper.c
@@ -261,8 +261,8 @@ int create_helper(int event_fd, int err_
data.hostname_len + data.ed_len + data.clid_len, 1))
continue;
- /* CLID into packet */
- for (p = daemon->packet, i = 0; i < data.clid_len; i++)
+ /* CLID into packet: limit to 100 bytes to avoid overflowing buffer. */
+ for (p = daemon->packet, i = 0; i < data.clid_len && i < 100; i++)
{
p += sprintf(p, "%.2x", buf[i]);
if (i != data.clid_len - 1)
@@ -0,0 +1,26 @@
commit 434d68f2eb1a58744470698483a3ae09b5a9a870
Author: Simon Kelley <simon@thekelleys.org.uk>
Date: Wed Mar 25 23:22:37 2026 +0000
Fix broken client subnet validation. CVE-2026-4893
Bug report from Royce M <royce@xchglabs.com>
Location: forward.c:713, edns0.c:421
With --add-subnet enabled, process_reply() passes the OPT record
length (~23 bytes) instead of the packet length to check_source().
All internal bounds checks fail, and the function always returns 1.
ECS source validation per RFC 7871 Section 9.2 is completely bypassed.
--- a/src/forward.c
+++ b/src/forward.c
@@ -710,7 +710,7 @@ static size_t process_reply(struct dns_h
/* Get extended RCODE. */
rcode |= sizep[2] << 4;
- if (option_bool(OPT_CLIENT_SUBNET) && !check_source(header, plen, pheader, query_source))
+ if (option_bool(OPT_CLIENT_SUBNET) && !check_source(header, n, pheader, query_source))
{
my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch"));
return 0;
@@ -0,0 +1,26 @@
commit fa3c8ddef6712b52f562813317e6a997e1210123
Author: Simon Kelley <simon@thekelleys.org.uk>
Date: Mon Mar 30 16:24:33 2026 +0100
Fix buffer overflow vulnerability in extract_addresses() CVE-2026-5172
Thanks to Hugo Martinez Ray for spotting this.
The value of rdlen for an RR can be a lie, allowing the
call to extract_name() at rfc1025.c:952 to advance the value of p1
past the calculated end of the record. The makes the calculation
of bytes remaining in the RR underflow to a huge number and results
in a massive heap OOB read and certain crash.
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -932,7 +932,8 @@ int extract_addresses(struct dns_header
/* Name, extract it then re-encode. */
int len;
- if (!extract_name(header, qlen, &p1, name, 1, 0))
+ /* rdlen may lie, and extract_name() advances p1 past where it says the record ends. */
+ if (!extract_name(header, qlen, &p1, name, 1, 0) || (p1 > endrr))
{
blockdata_free(addr.rrblock.rrdata);
return 2;
+1 -1
View File
@@ -231,7 +231,7 @@ CONFIG_MUTEX_SPIN_ON_OWNER=y
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_NEED_SG_DMA_LENGTH=y
CONFIG_NET_AIROHA=y
CONFIG_NET_AIROHA_FLOW_STATS=y
# CONFIG_NET_AIROHA_FLOW_STATS is not set
CONFIG_NET_AIROHA_NPU=y
CONFIG_NET_DEVLINK=y
CONFIG_NET_DSA=y
@@ -7,6 +7,7 @@
#include <dt-bindings/leds/common.h>
#include <dt-bindings/gpio/gpio.h>
#include "an7581.dtsi"
#include "an7581-npu-mt7996.dtsi"
/ {
model = "Airoha AN7581 Evaluation Board";
@@ -214,6 +215,10 @@
};
};
&npu {
status = "okay";
};
&eth {
status = "okay";
};
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
&npu {
firmware-name = "airoha/en7581_npu_rv32.bin",
"airoha/en7581_npu_data.bin";
};
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
&npu {
firmware-name = "airoha/en7581_MT7996_npu_rv32.bin",
"airoha/en7581_MT7996_npu_data.bin";
};
+11 -6
View File
@@ -54,6 +54,11 @@
no-map;
reg = <0x0 0x90c00000 0x0 0x6800>;
};
npu_ba: npu-ba@90c06800 {
no-map;
reg = <0x0 0x90c06800 0x0 0x200000>;
};
};
psci {
@@ -706,6 +711,8 @@
mediatek,pbus-csr = <&pbus_csr 0x0 0x4>;
airoha,np-scu = <&scuclk>;
interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
bus-range = <0x00 0xff>;
#interrupt-cells = <1>;
@@ -792,6 +799,8 @@
mediatek,pbus-csr = <&pbus_csr 0x10 0x14>;
airoha,np-scu = <&scuclk>;
interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
bus-range = <0x00 0xff>;
#interrupt-cells = <1>;
@@ -829,9 +838,9 @@
<GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
memory-region = <&npu_binary>, <&npu_pkt>, <&npu_txpkt>,
<&npu_txbufid>;
<&npu_txbufid>, <&npu_ba>;
memory-region-names = "binary", "pkt", "tx-pkt",
"tx-bufid";
"tx-bufid", "ba";
status = "disabled";
};
@@ -972,7 +981,6 @@
gsw_phy1: ethernet-phy@9 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <9>;
interrupts = <1>;
phy-mode = "internal";
status = "disabled";
@@ -995,7 +1003,6 @@
gsw_phy2: ethernet-phy@a {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <10>;
interrupts = <2>;
phy-mode = "internal";
status = "disabled";
@@ -1018,7 +1025,6 @@
gsw_phy3: ethernet-phy@b {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <11>;
interrupts = <3>;
phy-mode = "internal";
status = "disabled";
@@ -1041,7 +1047,6 @@
gsw_phy4: ethernet-phy@c {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <12>;
interrupts = <4>;
phy-mode = "internal";
status = "disabled";
+3
View File
@@ -207,6 +207,9 @@ CONFIG_OF_GPIO=y
CONFIG_OF_IRQ=y
CONFIG_OF_KOBJ=y
CONFIG_OF_MDIO=y
# CONFIG_MDIO_AIROHA is not set
# CONFIG_PCS_AIROHA_AN7581 is not set
# CONFIG_PCS_AIROHA_AN7583 is not set
CONFIG_OLD_SIGACTION=y
CONFIG_OLD_SIGSUSPEND3=y
CONFIG_OUTER_CACHE=y
@@ -0,0 +1,34 @@
From a7fc8c641cab855824c45e5e8877e40fd528b5df Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Fri, 2 Jan 2026 12:29:38 +0100
Subject: [PATCH] net: airoha: Fix npu rx DMA definitions
Fix typos in npu rx DMA descriptor definitions.
Fixes: b3ef7bdec66fb ("net: airoha: Add airoha_offload.h header")
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260102-airoha-npu-dma-rx-def-fixes-v1-1-205fc6bf7d94@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
include/linux/soc/airoha/airoha_offload.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
--- a/include/linux/soc/airoha/airoha_offload.h
+++ b/include/linux/soc/airoha/airoha_offload.h
@@ -70,12 +70,12 @@ static inline void airoha_ppe_dev_check_
#define NPU_RX1_DESC_NUM 512
/* CTRL */
-#define NPU_RX_DMA_DESC_LAST_MASK BIT(29)
-#define NPU_RX_DMA_DESC_LEN_MASK GENMASK(28, 15)
-#define NPU_RX_DMA_DESC_CUR_LEN_MASK GENMASK(14, 1)
+#define NPU_RX_DMA_DESC_LAST_MASK BIT(27)
+#define NPU_RX_DMA_DESC_LEN_MASK GENMASK(26, 14)
+#define NPU_RX_DMA_DESC_CUR_LEN_MASK GENMASK(13, 1)
#define NPU_RX_DMA_DESC_DONE_MASK BIT(0)
/* INFO */
-#define NPU_RX_DMA_PKT_COUNT_MASK GENMASK(31, 28)
+#define NPU_RX_DMA_PKT_COUNT_MASK GENMASK(31, 29)
#define NPU_RX_DMA_PKT_ID_MASK GENMASK(28, 26)
#define NPU_RX_DMA_SRC_PORT_MASK GENMASK(25, 21)
#define NPU_RX_DMA_CRSN_MASK GENMASK(20, 16)
@@ -0,0 +1,89 @@
From 5e7365b5a1ac8f517a7a84442289d7de242deb76 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sun, 14 Dec 2025 10:30:07 +0100
Subject: [PATCH] net: airoha: Move net_devs registration in a dedicated
routine
Since airoha_probe() is not executed under rtnl lock, there is small race
where a given device is configured by user-space while the remaining ones
are not completely loaded from the dts yet. This condition will allow a
hw device misconfiguration since there are some conditions (e.g. GDM2 check
in airoha_dev_init()) that require all device are properly loaded from the
device tree. Fix the issue moving net_devices registration at the end of
the airoha_probe routine.
Fixes: 9cd451d414f6e ("net: airoha: Add loopback support for GDM2")
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20251214-airoha-fix-dev-registration-v1-1-860e027ad4c6@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 39 ++++++++++++++++--------
1 file changed, 26 insertions(+), 13 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2925,19 +2925,26 @@ static int airoha_alloc_gdm_port(struct
port->id = id;
eth->ports[p] = port;
- err = airoha_metadata_dst_alloc(port);
- if (err)
- return err;
+ return airoha_metadata_dst_alloc(port);
+}
- err = register_netdev(dev);
- if (err)
- goto free_metadata_dst;
+static int airoha_register_gdm_devices(struct airoha_eth *eth)
+{
+ int i;
- return 0;
+ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
+ struct airoha_gdm_port *port = eth->ports[i];
+ int err;
+
+ if (!port)
+ continue;
+
+ err = register_netdev(port->dev);
+ if (err)
+ return err;
+ }
-free_metadata_dst:
- airoha_metadata_dst_free(port);
- return err;
+ return 0;
}
static int airoha_probe(struct platform_device *pdev)
@@ -3028,6 +3035,10 @@ static int airoha_probe(struct platform_
}
}
+ err = airoha_register_gdm_devices(eth);
+ if (err)
+ goto error_napi_stop;
+
return 0;
error_napi_stop:
@@ -3041,10 +3052,12 @@ error_hw_cleanup:
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
- if (port && port->dev->reg_state == NETREG_REGISTERED) {
+ if (!port)
+ continue;
+
+ if (port->dev->reg_state == NETREG_REGISTERED)
unregister_netdev(port->dev);
- airoha_metadata_dst_free(port);
- }
+ airoha_metadata_dst_free(port);
}
free_netdev(eth->napi_dev);
platform_set_drvdata(pdev, NULL);
@@ -0,0 +1,132 @@
From 4d513329b87c1bd0546d9f0288794e244322daa6 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Mon, 5 Jan 2026 10:40:47 +0100
Subject: [PATCH] net: airoha: Use gdm port enum value whenever possible
Use AIROHA_GDMx_IDX enum value whenever possible.
This patch is just cosmetic changes and does not introduce any logic one.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260105-airoha-use-port-idx-enum-v1-1-503ca5763858@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 40 +++++++++++++-----------
1 file changed, 21 insertions(+), 19 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -108,11 +108,11 @@ static int airoha_set_vip_for_gdm_port(s
u32 vip_port;
switch (port->id) {
- case 3:
+ case AIROHA_GDM3_IDX:
/* FIXME: handle XSI_PCIE1_PORT */
vip_port = XSI_PCIE0_VIP_PORT_MASK;
break;
- case 4:
+ case AIROHA_GDM4_IDX:
/* FIXME: handle XSI_USB_PORT */
vip_port = XSI_ETH_VIP_PORT_MASK;
break;
@@ -514,8 +514,8 @@ static int airoha_fe_init(struct airoha_
FIELD_PREP(IP_ASSEMBLE_PORT_MASK, 0) |
FIELD_PREP(IP_ASSEMBLE_NBQ_MASK, 22));
- airoha_fe_set(eth, REG_GDM_FWD_CFG(3), GDM_PAD_EN_MASK);
- airoha_fe_set(eth, REG_GDM_FWD_CFG(4), GDM_PAD_EN_MASK);
+ airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM3_IDX), GDM_PAD_EN_MASK);
+ airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM4_IDX), GDM_PAD_EN_MASK);
airoha_fe_crsn_qsel_init(eth);
@@ -1691,27 +1691,29 @@ static int airhoha_set_gdm2_loopback(str
/* Forward the traffic to the proper GDM port */
pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3
: FE_PSE_PORT_GDM4;
- airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(2), pse_port);
- airoha_fe_clear(eth, REG_GDM_FWD_CFG(2), GDM_STRIP_CRC_MASK);
+ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+ pse_port);
+ airoha_fe_clear(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+ GDM_STRIP_CRC_MASK);
/* Enable GDM2 loopback */
- airoha_fe_wr(eth, REG_GDM_TXCHN_EN(2), 0xffffffff);
- airoha_fe_wr(eth, REG_GDM_RXCHN_EN(2), 0xffff);
+ airoha_fe_wr(eth, REG_GDM_TXCHN_EN(AIROHA_GDM2_IDX), 0xffffffff);
+ airoha_fe_wr(eth, REG_GDM_RXCHN_EN(AIROHA_GDM2_IDX), 0xffff);
chan = port->id == AIROHA_GDM3_IDX ? airoha_is_7581(eth) ? 4 : 3 : 0;
- airoha_fe_rmw(eth, REG_GDM_LPBK_CFG(2),
+ airoha_fe_rmw(eth, REG_GDM_LPBK_CFG(AIROHA_GDM2_IDX),
LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK,
FIELD_PREP(LPBK_CHAN_MASK, chan) |
LBK_GAP_MODE_MASK | LBK_LEN_MODE_MASK |
LBK_CHAN_MODE_MASK | LPBK_EN_MASK);
- airoha_fe_rmw(eth, REG_GDM_LEN_CFG(2),
+ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(AIROHA_GDM2_IDX),
GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_MTU));
/* Disable VIP and IFC for GDM2 */
- airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(2));
- airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(2));
+ airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
+ airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));
/* XXX: handle XSI_USB_PORT and XSI_PCE1_PORT */
nbq = port->id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
@@ -1747,8 +1749,8 @@ static int airoha_dev_init(struct net_de
airoha_set_macaddr(port, dev->dev_addr);
switch (port->id) {
- case 3:
- case 4:
+ case AIROHA_GDM3_IDX:
+ case AIROHA_GDM4_IDX:
/* If GDM2 is active we can't enable loopback */
if (!eth->ports[1]) {
int err;
@@ -1758,7 +1760,7 @@ static int airoha_dev_init(struct net_de
return err;
}
fallthrough;
- case 2:
+ case AIROHA_GDM2_IDX:
if (airoha_ppe_is_enabled(eth, 1)) {
/* For PPE2 always use secondary cpu port. */
fe_cpu_port = FE_PSE_PORT_CDM2;
@@ -3102,14 +3104,14 @@ static const char * const en7581_xsi_rst
static int airoha_en7581_get_src_port_id(struct airoha_gdm_port *port, int nbq)
{
switch (port->id) {
- case 3:
+ case AIROHA_GDM3_IDX:
/* 7581 SoC supports PCIe serdes on GDM3 port */
if (nbq == 4)
return HSGMII_LAN_7581_PCIE0_SRCPORT;
if (nbq == 5)
return HSGMII_LAN_7581_PCIE1_SRCPORT;
break;
- case 4:
+ case AIROHA_GDM4_IDX:
/* 7581 SoC supports eth and usb serdes on GDM4 port */
if (!nbq)
return HSGMII_LAN_7581_ETH_SRCPORT;
@@ -3133,12 +3135,12 @@ static const char * const an7583_xsi_rst
static int airoha_an7583_get_src_port_id(struct airoha_gdm_port *port, int nbq)
{
switch (port->id) {
- case 3:
+ case AIROHA_GDM3_IDX:
/* 7583 SoC supports eth serdes on GDM3 port */
if (!nbq)
return HSGMII_LAN_7583_ETH_SRCPORT;
break;
- case 4:
+ case AIROHA_GDM4_IDX:
/* 7583 SoC supports PCIe and USB serdes on GDM4 port */
if (!nbq)
return HSGMII_LAN_7583_PCIE_SRCPORT;
@@ -0,0 +1,36 @@
From e4bc5dd53bf5d46cd58f081ffccc3809e2be5373 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Mon, 5 Jan 2026 09:49:16 +0100
Subject: [PATCH] net: airoha: npu: Dump fw version during probe
Dump firmware version running on the npu during module probe.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260105-airoha-npu-dump-fw-v1-1-36d8326975f8@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_npu.c | 6 ++++++
1 file changed, 6 insertions(+)
--- a/drivers/net/ethernet/airoha/airoha_npu.c
+++ b/drivers/net/ethernet/airoha/airoha_npu.c
@@ -658,6 +658,7 @@ static int airoha_npu_probe(struct platf
struct device_node *np;
void __iomem *base;
int i, irq, err;
+ u32 val;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
@@ -757,6 +758,11 @@ static int airoha_npu_probe(struct platf
regmap_write(npu->regmap, REG_CR_BOOT_TRIGGER, 0x1);
msleep(100);
+ if (!airoha_npu_wlan_msg_get(npu, 0, WLAN_FUNC_GET_WAIT_NPU_VERSION,
+ &val, sizeof(val), GFP_KERNEL))
+ dev_info(dev, "NPU fw version: %0d.%d\n",
+ (val >> 16) & 0xffff, val & 0xffff);
+
platform_set_drvdata(pdev, npu);
return 0;
@@ -0,0 +1,41 @@
From 6abcf751bc084804a9e5b3051442e8a2ce67f48a Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Mon, 5 Jan 2026 09:43:31 +0100
Subject: [PATCH] net: airoha: Fix schedule while atomic in airoha_ppe_deinit()
airoha_ppe_deinit() runs airoha_npu_ppe_deinit() in atomic context.
airoha_npu_ppe_deinit routine allocates ppe_data buffer with GFP_KERNEL
flag. Rely on rcu_replace_pointer in airoha_ppe_deinit routine in order
to fix schedule while atomic issue in airoha_npu_ppe_deinit() since we
do not need atomic context there.
Fixes: 00a7678310fe3 ("net: airoha: Introduce flowtable offload support")
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260105-airoha-fw-ethtool-v2-1-3b32b158cc31@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
drivers/net/ethernet/airoha/airoha_ppe.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -1547,13 +1547,16 @@ void airoha_ppe_deinit(struct airoha_eth
{
struct airoha_npu *npu;
- rcu_read_lock();
- npu = rcu_dereference(eth->npu);
+ mutex_lock(&flow_offload_mutex);
+
+ npu = rcu_replace_pointer(eth->npu, NULL,
+ lockdep_is_held(&flow_offload_mutex));
if (npu) {
npu->ops.ppe_deinit(npu);
airoha_npu_put(npu);
}
- rcu_read_unlock();
+
+ mutex_unlock(&flow_offload_mutex);
rhashtable_destroy(&eth->ppe->l2_flows);
rhashtable_destroy(&eth->flow_table);
@@ -0,0 +1,26 @@
From 50e194b6da721e4fa1fc6ebcf5969803c214929a Mon Sep 17 00:00:00 2001
From: Aleksander Jan Bajkowski <olek2@wp.pl>
Date: Sat, 10 Jan 2026 18:02:05 +0100
Subject: [PATCH] net: airoha: implement get_link_ksettings
Implement the .get_link_ksettings to get the rate, duplex, and
auto-negotiation status.
Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl>
Tested-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260110170212.570793-1-olek2@wp.pl
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 1 +
1 file changed, 1 insertion(+)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2806,6 +2806,7 @@ static const struct ethtool_ops airoha_e
.get_drvinfo = airoha_ethtool_get_drvinfo,
.get_eth_mac_stats = airoha_ethtool_get_mac_stats,
.get_rmon_stats = airoha_ethtool_get_rmon_stats,
+ .get_link_ksettings = phy_ethtool_get_link_ksettings,
.get_link = ethtool_op_get_link,
};
@@ -0,0 +1,32 @@
From 875a59c9a9e584d99d8e9e5aa8435ec9300bfe91 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Thu, 8 Jan 2026 16:05:08 +0100
Subject: [PATCH] net: airoha: npu: Init BA memory region if provided via DTS
Initialize NPU Block Ack memory region if reserved via DTS.
Block Ack memory region is used by NPU MT7996 (Eagle) offloading.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260108-airoha-ba-memory-region-v3-2-bf1814e5dcc4@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_npu.c | 8 ++++++++
1 file changed, 8 insertions(+)
--- a/drivers/net/ethernet/airoha/airoha_npu.c
+++ b/drivers/net/ethernet/airoha/airoha_npu.c
@@ -519,6 +519,14 @@ static int airoha_npu_wlan_init_memory(s
if (err)
return err;
+ if (of_property_match_string(npu->dev->of_node, "memory-region-names",
+ "ba") >= 0) {
+ cmd = WLAN_FUNC_SET_WAIT_DRAM_BA_NODE_ADDR;
+ err = airoha_npu_wlan_set_reserved_memory(npu, 0, "ba", cmd);
+ if (err)
+ return err;
+ }
+
cmd = WLAN_FUNC_SET_WAIT_IS_FORCE_TO_CPU;
return airoha_npu_wlan_msg_send(npu, 0, cmd, &val, sizeof(val),
GFP_KERNEL);
@@ -0,0 +1,37 @@
From 6406fc709ace081575de2a8a7eee12e63d4c96c6 Mon Sep 17 00:00:00 2001
From: Sayantan Nandy <sayantann11@gmail.com>
Date: Mon, 19 Jan 2026 13:06:58 +0530
Subject: [PATCH] net: airoha_eth: increase max MTU to 9220 for DSA jumbo
frames
The industry standard jumbo frame MTU is 9216 bytes. When using the DSA
subsystem, a 4-byte tag is added to each Ethernet frame.
Increase AIROHA_MAX_MTU to 9220 bytes (9216 + 4) so that users can set a
standard 9216-byte MTU on DSA ports.
The underlying hardware supports significantly larger frame sizes
(approximately 16K). However, the maximum MTU is limited to 9220 bytes
for now, as this is sufficient to support standard jumbo frames and does
not incur additional memory allocation overhead.
Signed-off-by: Sayantan Nandy <sayantann11@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260119073658.6216-1-sayantann11@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -21,7 +21,7 @@
#define AIROHA_MAX_NUM_IRQ_BANKS 4
#define AIROHA_MAX_DSA_PORTS 7
#define AIROHA_MAX_NUM_RSTS 3
-#define AIROHA_MAX_MTU 9216
+#define AIROHA_MAX_MTU 9220
#define AIROHA_MAX_PACKET_SIZE 2048
#define AIROHA_NUM_QOS_CHANNELS 4
#define AIROHA_NUM_QOS_QUEUES 8
@@ -1,410 +0,0 @@
From 527123b53739a2f73ca924b9c6e2f63dc66739a5 Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Fri, 1 Aug 2025 11:06:56 +0200
Subject: [PATCH 1/3] ASoC: mediatek: move some header to global include
In preparation for support of Airoha SoC sound system based on Mediatek
AFE, move some header to global include to prevent having to use complex
redirection for inclusion.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
.../common => include/sound/mediatek}/mtk-afe-fe-dai.h | 0
.../sound/mediatek}/mtk-afe-platform-driver.h | 0
sound/soc/mediatek/common/mtk-afe-fe-dai.c | 4 ++--
sound/soc/mediatek/common/mtk-afe-platform-driver.c | 2 +-
sound/soc/mediatek/mt2701/mt2701-afe-pcm.c | 4 ++--
sound/soc/mediatek/mt6797/mt6797-afe-pcm.c | 4 ++--
sound/soc/mediatek/mt7986/mt7986-afe-pcm.c | 4 ++--
sound/soc/mediatek/mt8173/mt8173-afe-pcm.c | 4 ++--
sound/soc/mediatek/mt8183/mt8183-afe-pcm.c | 4 ++--
sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c | 2 +-
sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c | 2 +-
sound/soc/mediatek/mt8186/mt8186-afe-pcm.c | 4 ++--
sound/soc/mediatek/mt8186/mt8186-misc-control.c | 4 ++--
sound/soc/mediatek/mt8186/mt8186-mt6366-common.c | 2 +-
sound/soc/mediatek/mt8186/mt8186-mt6366.c | 2 +-
sound/soc/mediatek/mt8188/mt8188-afe-pcm.c | 4 ++--
sound/soc/mediatek/mt8188/mt8188-mt6359.c | 2 +-
sound/soc/mediatek/mt8192/mt8192-afe-pcm.c | 4 ++--
sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c | 2 +-
sound/soc/mediatek/mt8195/mt8195-afe-pcm.c | 4 ++--
sound/soc/mediatek/mt8195/mt8195-mt6359.c | 2 +-
sound/soc/mediatek/mt8365/mt8365-afe-pcm.c | 4 ++--
22 files changed, 32 insertions(+), 32 deletions(-)
rename {sound/soc/mediatek/common => include/sound/mediatek}/mtk-afe-fe-dai.h (100%)
rename {sound/soc/mediatek/common => include/sound/mediatek}/mtk-afe-platform-driver.h (100%)
--- a/sound/soc/mediatek/common/mtk-afe-fe-dai.c
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
@@ -11,9 +11,9 @@
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/soc.h>
-#include "mtk-afe-platform-driver.h"
+#include <sound/mediatek/mtk-afe-platform-driver.h>
#include <sound/pcm_params.h>
-#include "mtk-afe-fe-dai.h"
+#include <sound/mediatek/mtk-afe-fe-dai.h>
#include "mtk-base-afe.h"
#define AFE_BASE_END_OFFSET 8
--- a/sound/soc/mediatek/common/mtk-afe-platform-driver.c
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
@@ -10,7 +10,7 @@
#include <linux/dma-mapping.h>
#include <sound/soc.h>
-#include "mtk-afe-platform-driver.h"
+#include <sound/mediatek/mtk-afe-platform-driver.h>
#include "mtk-base-afe.h"
int mtk_afe_combine_sub_dai(struct mtk_base_afe *afe)
--- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -18,8 +18,8 @@
#include "mt2701-afe-common.h"
#include "mt2701-afe-clock-ctrl.h"
-#include "../common/mtk-afe-platform-driver.h"
-#include "../common/mtk-afe-fe-dai.h"
+#include <sound/mediatek/mtk-afe-platform-driver.h>
+#include <sound/mediatek/mtk-afe-fe-dai.h>
static const struct snd_pcm_hardware mt2701_afe_hardware = {
.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED
--- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
+++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
@@ -16,8 +16,8 @@
#include "mt6797-afe-clk.h"
#include "mt6797-interconnection.h"
#include "mt6797-reg.h"
-#include "../common/mtk-afe-platform-driver.h"
-#include "../common/mtk-afe-fe-dai.h"
+#include <sound/mediatek/mtk-afe-platform-driver.h>
+#include <sound/mediatek/mtk-afe-fe-dai.h>
enum {
MTK_AFE_RATE_8K = 0,
--- a/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c
+++ b/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c
@@ -16,8 +16,8 @@
#include "mt7986-afe-common.h"
#include "mt7986-reg.h"
-#include "../common/mtk-afe-platform-driver.h"
-#include "../common/mtk-afe-fe-dai.h"
+#include <sound/mediatek/mtk-afe-platform-driver.h>
+#include <sound/mediatek/mtk-afe-fe-dai.h>
enum {
MTK_AFE_RATE_8K = 0,
--- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
@@ -19,8 +19,8 @@
#include <sound/soc.h>
#include "mt8173-afe-common.h"
#include "../common/mtk-base-afe.h"
-#include "../common/mtk-afe-platform-driver.h"
-#include "../common/mtk-afe-fe-dai.h"
+#include <sound/mediatek/mtk-afe-platform-driver.h>
+#include <sound/mediatek/mtk-afe-fe-dai.h>
/*****************************************************************************
* R E G I S T E R D E F I N I T I O N
--- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
+++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
@@ -18,8 +18,8 @@
#include "mt8183-afe-clk.h"
#include "mt8183-interconnection.h"
#include "mt8183-reg.h"
-#include "../common/mtk-afe-platform-driver.h"
-#include "../common/mtk-afe-fe-dai.h"
+#include <sound/mediatek/mtk-afe-platform-driver.h>
+#include <sound/mediatek/mtk-afe-fe-dai.h>
enum {
MTK_AFE_RATE_8K = 0,
--- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
+++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
@@ -16,7 +16,7 @@
#include "../../codecs/da7219.h"
#include "../../codecs/rt1015.h"
-#include "../common/mtk-afe-platform-driver.h"
+#include <sound/mediatek/mtk-afe-platform-driver.h>
#include "mt8183-afe-common.h"
#define DA7219_CODEC_DAI "da7219-hifi"
--- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
+++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
@@ -15,7 +15,7 @@
#include "../../codecs/rt1015.h"
#include "../../codecs/ts3a227e.h"
-#include "../common/mtk-afe-platform-driver.h"
+#include <sound/mediatek/mtk-afe-platform-driver.h>
#include "mt8183-afe-common.h"
#define RT1015_CODEC_DAI "rt1015-aif"
--- a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
@@ -15,8 +15,8 @@
#include <linux/reset.h>
#include <sound/soc.h>
-#include "../common/mtk-afe-platform-driver.h"
-#include "../common/mtk-afe-fe-dai.h"
+#include <sound/mediatek/mtk-afe-platform-driver.h>
+#include <sound/mediatek/mtk-afe-fe-dai.h>
#include "mt8186-afe-common.h"
#include "mt8186-afe-clk.h"
--- a/sound/soc/mediatek/mt8186/mt8186-misc-control.c
+++ b/sound/soc/mediatek/mt8186/mt8186-misc-control.c
@@ -11,8 +11,8 @@
#include <linux/regmap.h>
#include <sound/soc.h>
-#include "../common/mtk-afe-fe-dai.h"
-#include "../common/mtk-afe-platform-driver.h"
+#include <sound/mediatek/mtk-afe-fe-dai.h>
+#include <sound/mediatek/mtk-afe-platform-driver.h>
#include "mt8186-afe-common.h"
static const char * const mt8186_sgen_mode_str[] = {
--- a/sound/soc/mediatek/mt8186/mt8186-mt6366-common.c
+++ b/sound/soc/mediatek/mt8186/mt8186-mt6366-common.c
@@ -9,7 +9,7 @@
#include <sound/soc.h>
#include "../../codecs/mt6358.h"
-#include "../common/mtk-afe-platform-driver.h"
+#include <sound/mediatek/mtk-afe-platform-driver.h>
#include "mt8186-afe-common.h"
#include "mt8186-mt6366-common.h"
--- a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
+++ b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
@@ -24,8 +24,8 @@
#include "mt8188-afe-common.h"
#include "mt8188-afe-clk.h"
#include "mt8188-reg.h"
-#include "../common/mtk-afe-platform-driver.h"
-#include "../common/mtk-afe-fe-dai.h"
+#include <sound/mediatek/mtk-afe-platform-driver.h>
+#include <sound/mediatek/mtk-afe-fe-dai.h>
#define MT8188_MEMIF_BUFFER_BYTES_ALIGN (0x40)
#define MT8188_MEMIF_DL7_MAX_PERIOD_SIZE (0x3fff)
--- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
+++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
@@ -17,8 +17,8 @@
#include <linux/reset.h>
#include <sound/soc.h>
-#include "../common/mtk-afe-fe-dai.h"
-#include "../common/mtk-afe-platform-driver.h"
+#include <sound/mediatek/mtk-afe-fe-dai.h>
+#include <sound/mediatek/mtk-afe-platform-driver.h>
#include "mt8192-afe-common.h"
#include "mt8192-afe-clk.h"
--- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
+++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
@@ -20,8 +20,8 @@
#include "mt8195-afe-common.h"
#include "mt8195-afe-clk.h"
#include "mt8195-reg.h"
-#include "../common/mtk-afe-platform-driver.h"
-#include "../common/mtk-afe-fe-dai.h"
+#include <sound/mediatek/mtk-afe-platform-driver.h>
+#include <sound/mediatek/mtk-afe-fe-dai.h>
#define MT8195_MEMIF_BUFFER_BYTES_ALIGN (0x40)
#define MT8195_MEMIF_DL7_MAX_PERIOD_SIZE (0x3fff)
--- a/sound/soc/mediatek/mt8195/mt8195-mt6359.c
+++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c
@@ -19,7 +19,7 @@
#include "../../codecs/mt6359.h"
#include "../../codecs/rt1011.h"
#include "../../codecs/rt5682.h"
-#include "../common/mtk-afe-platform-driver.h"
+#include <sound/mediatek/mtk-afe-platform-driver.h>
#include "../common/mtk-dsp-sof-common.h"
#include "../common/mtk-soc-card.h"
#include "mt8195-afe-clk.h"
--- /dev/null
+++ b/include/sound/mediatek/mtk-afe-fe-dai.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mtk-afe-fe-dais.h -- Mediatek afe fe dai operator definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ */
+
+#ifndef _MTK_AFE_FE_DAI_H_
+#define _MTK_AFE_FE_DAI_H_
+
+struct snd_soc_dai_ops;
+struct mtk_base_afe;
+struct mtk_base_afe_memif;
+
+int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai);
+
+extern const struct snd_soc_dai_ops mtk_afe_fe_ops;
+
+int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe);
+int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id);
+int mtk_afe_suspend(struct snd_soc_component *component);
+int mtk_afe_resume(struct snd_soc_component *component);
+
+int mtk_memif_set_enable(struct mtk_base_afe *afe, int id);
+int mtk_memif_set_disable(struct mtk_base_afe *afe, int id);
+int mtk_memif_set_addr(struct mtk_base_afe *afe, int id,
+ unsigned char *dma_area,
+ dma_addr_t dma_addr,
+ size_t dma_bytes);
+int mtk_memif_set_channel(struct mtk_base_afe *afe,
+ int id, unsigned int channel);
+int mtk_memif_set_rate(struct mtk_base_afe *afe,
+ int id, unsigned int rate);
+int mtk_memif_set_rate_substream(struct snd_pcm_substream *substream,
+ int id, unsigned int rate);
+int mtk_memif_set_format(struct mtk_base_afe *afe,
+ int id, snd_pcm_format_t format);
+int mtk_memif_set_pbuf_size(struct mtk_base_afe *afe,
+ int id, int pbuf_size);
+#endif
--- /dev/null
+++ b/include/sound/mediatek/mtk-afe-platform-driver.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mtk-afe-platform-driver.h -- Mediatek afe platform driver definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ */
+
+#ifndef _MTK_AFE_PLATFORM_DRIVER_H_
+#define _MTK_AFE_PLATFORM_DRIVER_H_
+
+#define AFE_PCM_NAME "mtk-afe-pcm"
+extern const struct snd_soc_component_driver mtk_afe_pcm_platform;
+
+struct mtk_base_afe;
+struct snd_pcm;
+struct snd_soc_component;
+struct snd_soc_pcm_runtime;
+
+snd_pcm_uframes_t mtk_afe_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+int mtk_afe_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd);
+
+int mtk_afe_combine_sub_dai(struct mtk_base_afe *afe);
+int mtk_afe_add_sub_dai_control(struct snd_soc_component *component);
+#endif
+
--- a/sound/soc/mediatek/common/mtk-afe-fe-dai.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * mtk-afe-fe-dais.h -- Mediatek afe fe dai operator definition
- *
- * Copyright (c) 2016 MediaTek Inc.
- * Author: Garlic Tseng <garlic.tseng@mediatek.com>
- */
-
-#ifndef _MTK_AFE_FE_DAI_H_
-#define _MTK_AFE_FE_DAI_H_
-
-struct snd_soc_dai_ops;
-struct mtk_base_afe;
-struct mtk_base_afe_memif;
-
-int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai);
-void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai);
-int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai);
-int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai);
-int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai);
-int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai);
-
-extern const struct snd_soc_dai_ops mtk_afe_fe_ops;
-
-int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe);
-int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id);
-int mtk_afe_suspend(struct snd_soc_component *component);
-int mtk_afe_resume(struct snd_soc_component *component);
-
-int mtk_memif_set_enable(struct mtk_base_afe *afe, int id);
-int mtk_memif_set_disable(struct mtk_base_afe *afe, int id);
-int mtk_memif_set_addr(struct mtk_base_afe *afe, int id,
- unsigned char *dma_area,
- dma_addr_t dma_addr,
- size_t dma_bytes);
-int mtk_memif_set_channel(struct mtk_base_afe *afe,
- int id, unsigned int channel);
-int mtk_memif_set_rate(struct mtk_base_afe *afe,
- int id, unsigned int rate);
-int mtk_memif_set_rate_substream(struct snd_pcm_substream *substream,
- int id, unsigned int rate);
-int mtk_memif_set_format(struct mtk_base_afe *afe,
- int id, snd_pcm_format_t format);
-int mtk_memif_set_pbuf_size(struct mtk_base_afe *afe,
- int id, int pbuf_size);
-#endif
--- a/sound/soc/mediatek/common/mtk-afe-platform-driver.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * mtk-afe-platform-driver.h -- Mediatek afe platform driver definition
- *
- * Copyright (c) 2016 MediaTek Inc.
- * Author: Garlic Tseng <garlic.tseng@mediatek.com>
- */
-
-#ifndef _MTK_AFE_PLATFORM_DRIVER_H_
-#define _MTK_AFE_PLATFORM_DRIVER_H_
-
-#define AFE_PCM_NAME "mtk-afe-pcm"
-extern const struct snd_soc_component_driver mtk_afe_pcm_platform;
-
-struct mtk_base_afe;
-struct snd_pcm;
-struct snd_soc_component;
-struct snd_soc_pcm_runtime;
-
-snd_pcm_uframes_t mtk_afe_pcm_pointer(struct snd_soc_component *component,
- struct snd_pcm_substream *substream);
-int mtk_afe_pcm_new(struct snd_soc_component *component,
- struct snd_soc_pcm_runtime *rtd);
-
-int mtk_afe_combine_sub_dai(struct mtk_base_afe *afe);
-int mtk_afe_add_sub_dai_control(struct snd_soc_component *component);
-#endif
-
@@ -1,729 +0,0 @@
From 9989af6ed0dba86f57ac4aa1574f9ce9b1e640af Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Thu, 31 Jul 2025 15:32:32 +0200
Subject: [PATCH 2/3] ASoC: airoha: Add AFE and I2S driver for Airoha AN7581
Add support for the Sound system present on Airoha AN7581 SoC. This is
based on the mediatek AFE drivers.
Also add the I2S driver to create an actual sound card for the AFE.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
sound/soc/Kconfig | 1 +
sound/soc/Makefile | 1 +
sound/soc/airoha/Kconfig | 19 +
sound/soc/airoha/Makefile | 2 +
sound/soc/airoha/an7581/Makefile | 8 +
sound/soc/airoha/an7581/an7581-afe-common.h | 35 ++
sound/soc/airoha/an7581/an7581-afe-pcm.c | 455 ++++++++++++++++++++
sound/soc/airoha/an7581/an7581-i2s.c | 110 +++++
sound/soc/airoha/an7581/an7581-reg.h | 29 ++
9 files changed, 660 insertions(+)
create mode 100644 sound/soc/airoha/Kconfig
create mode 100644 sound/soc/airoha/Makefile
create mode 100644 sound/soc/airoha/an7581/Makefile
create mode 100644 sound/soc/airoha/an7581/an7581-afe-common.h
create mode 100644 sound/soc/airoha/an7581/an7581-afe-pcm.c
create mode 100644 sound/soc/airoha/an7581/an7581-i2s.c
create mode 100644 sound/soc/airoha/an7581/an7581-reg.h
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -78,6 +78,7 @@ config SND_SOC_ACPI
# All the supported SoCs
source "sound/soc/adi/Kconfig"
+source "sound/soc/airoha/Kconfig"
source "sound/soc/amd/Kconfig"
source "sound/soc/apple/Kconfig"
source "sound/soc/atmel/Kconfig"
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_SND_SOC) += codecs/
obj-$(CONFIG_SND_SOC) += generic/
obj-$(CONFIG_SND_SOC) += apple/
obj-$(CONFIG_SND_SOC) += adi/
+obj-$(CONFIG_SND_SOC) += airoha/
obj-$(CONFIG_SND_SOC) += amd/
obj-$(CONFIG_SND_SOC) += atmel/
obj-$(CONFIG_SND_SOC) += au1x/
--- /dev/null
+++ b/sound/soc/airoha/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SND_SOC_AN7581
+ tristate "ASoC support for Airoha AN7581 chip"
+ depends on ARCH_AIROHA || COMPILE_TEST
+ select SND_SOC_MEDIATEK
+ help
+ This adds ASoC driver for Airoha AN7581 boards
+ that can be used with other codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_AN7581_I2S
+ tristate "I2S support for Airoha AN7581 chip"
+ depends on SND_SOC_AN7581
+ help
+ This adds I2S driver for Airoha AN7581 boards
+ that can be used with other codecs.
+ Select Y if you have such device.
+ If unsure select "N".
--- /dev/null
+++ b/sound/soc/airoha/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_SND_SOC_AN7581) += an7581/
--- /dev/null
+++ b/sound/soc/airoha/an7581/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# platform driver
+snd-soc-an7581-afe-y := \
+ an7581-afe-pcm.o
+
+obj-$(CONFIG_SND_SOC_AN7581) += snd-soc-an7581-afe.o
+obj-$(CONFIG_SND_SOC_AN7581_I2S) += an7581-i2s.o
--- /dev/null
+++ b/sound/soc/airoha/an7581/an7581-afe-common.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * an7581-afe-common.h -- Airoha AN7581 audio driver definitions
+ */
+
+#ifndef _AN7581_AFE_COMMON_H_
+#define _AN7581_AFE_COMMON_H_
+
+#include <sound/soc.h>
+#include <linux/list.h>
+#include <linux/regmap.h>
+#include "../../mediatek/common/mtk-base-afe.h"
+
+enum {
+ AN7581_MEMIF_DL1,
+ AN7581_MEMIF_UL1,
+ AN7581_MEMIF_NUM,
+ AN7581_DAI_NUM = AN7581_MEMIF_NUM,
+};
+
+enum {
+ AN7581_IRQ_0,
+ AN7581_IRQ_1,
+ AN7581_IRQ_NUM,
+};
+
+struct an7581_afe_private {
+ /* dai */
+ void *dai_priv[AN7581_DAI_NUM];
+};
+
+unsigned int an7581_afe_rate_transform(struct device *dev,
+ unsigned int rate);
+
+#endif
--- /dev/null
+++ b/sound/soc/airoha/an7581/an7581-afe-pcm.c
@@ -0,0 +1,455 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Airoha ALSA SoC AFE platform driver for AN7581
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+
+#include "an7581-afe-common.h"
+#include "an7581-reg.h"
+#include <sound/mediatek/mtk-afe-platform-driver.h>
+#include <sound/mediatek/mtk-afe-fe-dai.h>
+
+enum {
+ ARH_AFE_RATE_7K = 16,
+ ARH_AFE_RATE_8K = 0,
+ ARH_AFE_RATE_11K = 17,
+ ARH_AFE_RATE_12K = 1,
+ ARH_AFE_RATE_14K = 18,
+ ARH_AFE_RATE_16K = 2,
+ ARH_AFE_RATE_22K = 19,
+ ARH_AFE_RATE_24K = 3,
+ ARH_AFE_RATE_29K = 20,
+ ARH_AFE_RATE_32K = 4,
+ ARH_AFE_RATE_44K = 21,
+ ARH_AFE_RATE_48K = 5,
+ ARH_AFE_RATE_88K = 22,
+ ARH_AFE_RATE_96K = 6,
+ ARH_AFE_RATE_176K = 23,
+ ARH_AFE_RATE_192K = 7,
+ ARH_AFE_RATE_352K = 24,
+ ARH_AFE_RATE_384K = 8,
+};
+
+unsigned int an7581_afe_rate_transform(struct device *dev, unsigned int rate)
+{
+ switch (rate) {
+ case 7350:
+ return ARH_AFE_RATE_7K;
+ case 8000:
+ return ARH_AFE_RATE_8K;
+ case 11025:
+ return ARH_AFE_RATE_11K;
+ case 12000:
+ return ARH_AFE_RATE_12K;
+ case 14700:
+ return ARH_AFE_RATE_14K;
+ case 16000:
+ return ARH_AFE_RATE_16K;
+ case 22050:
+ return ARH_AFE_RATE_22K;
+ case 24000:
+ return ARH_AFE_RATE_24K;
+ case 29400:
+ return ARH_AFE_RATE_29K;
+ case 32000:
+ return ARH_AFE_RATE_32K;
+ case 44100:
+ return ARH_AFE_RATE_44K;
+ case 48000:
+ return ARH_AFE_RATE_48K;
+ case 88200:
+ return ARH_AFE_RATE_88K;
+ case 96000:
+ return ARH_AFE_RATE_96K;
+ case 176400:
+ return ARH_AFE_RATE_176K;
+ case 192000:
+ return ARH_AFE_RATE_192K;
+ case 352800:
+ return ARH_AFE_RATE_352K;
+ case 384000:
+ return ARH_AFE_RATE_384K;
+ default:
+ dev_warn(dev, "%s(), rate %u invalid, using %d!!!\n",
+ __func__, rate, ARH_AFE_RATE_48K);
+ return ARH_AFE_RATE_48K;
+ }
+}
+
+static const int an7581_memif_specified_irqs[AN7581_MEMIF_NUM] = {
+ [AN7581_MEMIF_DL1] = AN7581_IRQ_0,
+ [AN7581_MEMIF_UL1] = AN7581_IRQ_1,
+};
+
+static const struct snd_pcm_hardware an7581_afe_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = 512,
+ .period_bytes_max = 128 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 256 * 1024,
+ .fifo_size = 0,
+};
+
+static int an7581_memif_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+
+ return an7581_afe_rate_transform(afe->dev, rate);
+}
+
+static int an7581_irq_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+
+ return an7581_afe_rate_transform(afe->dev, rate);
+}
+
+#define ARH_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver an7581_memif_dai_driver[] = {
+ /* FE DAIs: memory intefaces to CPU */
+ {
+ .name = "DL1",
+ .id = AN7581_MEMIF_DL1,
+ .playback = {
+ .stream_name = "DL1",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = ARH_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+ {
+ .name = "UL1",
+ .id = AN7581_MEMIF_UL1,
+ .capture = {
+ .stream_name = "UL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = ARH_PCM_FORMATS,
+ },
+ .ops = &mtk_afe_fe_ops,
+ },
+};
+
+static const struct snd_soc_component_driver an7581_afe_pcm_dai_component = {
+ .name = "an7581-afe-pcm-dai",
+};
+
+static const struct mtk_base_memif_data memif_data[AN7581_MEMIF_NUM] = {
+ [AN7581_MEMIF_DL1] = {
+ .name = "DL1",
+ .id = AN7581_MEMIF_DL1,
+ .reg_ofs_base = AFE_DL1_BASE,
+ .reg_ofs_cur = AFE_DL1_CUR,
+ .reg_ofs_end = AFE_DL1_END,
+ .fs_reg = -1,
+ .fs_shift = -1,
+ .fs_maskbit = -1,
+ .mono_reg = -1,
+ .mono_shift = -1,
+ .hd_reg = -1,
+ .hd_shift = -1,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 17,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ },
+ [AN7581_MEMIF_UL1] = {
+ .name = "UL1",
+ .id = AN7581_MEMIF_UL1,
+ .reg_ofs_base = AFE_UL1_BASE,
+ .reg_ofs_cur = AFE_UL1_CUR,
+ .reg_ofs_end = AFE_UL1_END,
+ .fs_reg = -1,
+ .fs_shift = -1,
+ .fs_maskbit = -1,
+ .mono_reg = -1,
+ .mono_shift = -1,
+ .hd_reg = -1,
+ .hd_shift = -1,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 1,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ },
+};
+
+static const struct mtk_base_irq_data irq_data[AN7581_IRQ_NUM] = {
+ [AN7581_IRQ_0] = {
+ .id = AN7581_IRQ_0,
+ .irq_cnt_reg = -1,
+ .irq_cnt_shift = -1,
+ .irq_cnt_maskbit = -1,
+ .irq_en_reg = AFE_IRQ1_CON0,
+ .irq_en_shift = 4,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = -1,
+ .irq_fs_maskbit = -1,
+ .irq_clr_reg = AFE_IRQ1_CON0,
+ .irq_clr_shift = 0,
+ },
+ [AN7581_IRQ_1] = {
+ .id = AN7581_IRQ_1,
+ .irq_cnt_reg = -1,
+ .irq_cnt_shift = -1,
+ .irq_cnt_maskbit = -1,
+ .irq_en_reg = AFE_IRQ0_CON0,
+ .irq_en_shift = 4,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = -1,
+ .irq_fs_maskbit = -1,
+ .irq_clr_reg = AFE_IRQ0_CON0,
+ .irq_clr_shift = 1,
+ },
+};
+
+static const struct regmap_config an7581_afe_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = AFE_MAX_REGISTER,
+ .num_reg_defaults_raw = ((AFE_MAX_REGISTER / 4) + 1),
+};
+
+static irqreturn_t an7581_afe_irq_handler(int irq_id, void *dev)
+{
+ struct mtk_base_afe *afe = dev;
+ struct mtk_base_afe_irq *irq;
+ u32 status;
+ u32 reg;
+ int i;
+
+ regmap_read(afe->regmap, AFE_IRQ_STS, &status);
+
+ if (status & AFE_IRQ_STS_RECORD)
+ reg = AFE_IRQ0_CON0;
+ else
+ reg = AFE_IRQ1_CON0;
+
+ regmap_set_bits(afe->regmap, reg, BIT(2));
+ regmap_clear_bits(afe->regmap, reg, BIT(2));
+
+ regmap_set_bits(afe->regmap, reg, BIT(3));
+ regmap_clear_bits(afe->regmap, reg, BIT(3));
+
+ for (i = 0; i < AN7581_MEMIF_NUM; i++) {
+ struct mtk_base_afe_memif *memif = &afe->memif[i];
+
+ if (!memif->substream)
+ continue;
+
+ if (memif->irq_usage < 0)
+ continue;
+
+ irq = &afe->irqs[memif->irq_usage];
+
+ if (status & (1 << irq->irq_data->irq_clr_shift))
+ snd_pcm_period_elapsed(memif->substream);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int an7581_afe_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int an7581_afe_runtime_resume(struct device *dev)
+{
+ return 0;
+}
+
+static int an7581_dai_memif_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = an7581_memif_dai_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(an7581_memif_dai_driver);
+
+ return 0;
+}
+
+typedef int (*dai_register_cb)(struct mtk_base_afe *);
+static const dai_register_cb dai_register_cbs[] = {
+ an7581_dai_memif_register,
+};
+
+static int an7581_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ struct mtk_base_afe *afe;
+ struct an7581_afe_private *afe_priv;
+ struct device *dev;
+ int i, irq_id, ret;
+
+ afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, afe);
+
+ afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+ GFP_KERNEL);
+ if (!afe->platform_priv)
+ return -ENOMEM;
+
+ afe_priv = afe->platform_priv;
+ afe->dev = &pdev->dev;
+ dev = afe->dev;
+
+ afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(afe->base_addr))
+ return PTR_ERR(afe->base_addr);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_get_sync(&pdev->dev);
+
+ afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
+ &an7581_afe_regmap_config);
+
+ pm_runtime_put_sync(&pdev->dev);
+ if (IS_ERR(afe->regmap))
+ return PTR_ERR(afe->regmap);
+
+ mutex_init(&afe->irq_alloc_lock);
+
+ /* irq initialize */
+ afe->irqs_size = AN7581_IRQ_NUM;
+ afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+ GFP_KERNEL);
+ if (!afe->irqs)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->irqs_size; i++)
+ afe->irqs[i].irq_data = &irq_data[i];
+
+ /* request irq */
+ irq_id = platform_get_irq(pdev, 0);
+ if (irq_id < 0)
+ return irq_id;
+
+ ret = devm_request_irq(dev, irq_id, an7581_afe_irq_handler,
+ IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request irq for asys-isr\n");
+
+ /* init memif */
+ afe->memif_size = AN7581_MEMIF_NUM;
+ afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+ GFP_KERNEL);
+ if (!afe->memif)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->memif_size; i++) {
+ int sel_irq = an7581_memif_specified_irqs[i];
+
+ afe->memif[i].data = &memif_data[i];
+ afe->memif[i].irq_usage = sel_irq;
+ afe->memif[i].const_irq = 1;
+ afe->irqs[sel_irq].irq_occupyed = true;
+ }
+
+ /* init sub_dais */
+ INIT_LIST_HEAD(&afe->sub_dais);
+
+ for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
+ ret = dai_register_cbs[i](afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "DAI register failed, i: %d\n", i);
+ }
+
+ /* init dai_driver and component_driver */
+ ret = mtk_afe_combine_sub_dai(afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "mtk_afe_combine_sub_dai fail\n");
+
+ afe->mtk_afe_hardware = &an7581_afe_hardware;
+ afe->memif_fs = an7581_memif_fs;
+ afe->irq_fs = an7581_irq_fs;
+
+ afe->runtime_resume = an7581_afe_runtime_resume;
+ afe->runtime_suspend = an7581_afe_runtime_suspend;
+
+ /* register component */
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &mtk_afe_pcm_platform,
+ NULL, 0);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot register AFE component\n");
+
+ ret = devm_snd_soc_register_component(afe->dev,
+ &an7581_afe_pcm_dai_component,
+ afe->dai_drivers,
+ afe->num_dai_drivers);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot register PCM DAI component\n");
+
+ return 0;
+}
+
+static void an7581_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ an7581_afe_runtime_suspend(&pdev->dev);
+}
+
+static const struct of_device_id an7581_afe_pcm_dt_match[] = {
+ { .compatible = "airoha,an7581-afe" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, an7581_afe_pcm_dt_match);
+
+static const struct dev_pm_ops an7581_afe_pm_ops = {
+ RUNTIME_PM_OPS(an7581_afe_runtime_suspend,
+ an7581_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver an7581_afe_pcm_driver = {
+ .driver = {
+ .name = "an7581-audio",
+ .of_match_table = an7581_afe_pcm_dt_match,
+ .pm = pm_ptr(&an7581_afe_pm_ops),
+ },
+ .probe = an7581_afe_pcm_dev_probe,
+ .remove_new = an7581_afe_pcm_dev_remove,
+};
+module_platform_driver(an7581_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Airoha SoC AFE platform driver for ALSA AN7581");
+MODULE_LICENSE("GPL");
--- /dev/null
+++ b/sound/soc/airoha/an7581/an7581-i2s.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Airoha ALSA SoC I2S platform driver for AN7581
+ *
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include "an7581-afe-common.h"
+
+SND_SOC_DAILINK_DEFS(playback,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+static struct snd_soc_dai_link an7581_i2s_dai_links[] = {
+ {
+ .name = "an7581-i2s-playback",
+ .stream_name = "an7581-i2s-playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 0,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback),
+ },
+ {
+ .name = "an7581-i2s-capture",
+ .stream_name = "an7581-i2s-capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 0,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture),
+ },
+};
+
+static struct snd_soc_card an7581_i2s_card = {
+ .name = "an7581-i2s",
+ .owner = THIS_MODULE,
+ .dai_link = an7581_i2s_dai_links,
+ .num_links = ARRAY_SIZE(an7581_i2s_dai_links),
+};
+
+static int an7581_i2s_machine_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &an7581_i2s_card;
+ struct device_node *platform_dai_node;
+ struct snd_soc_dai_link *dai_link;
+ struct device_node *platform;
+ int ret, i;
+
+ card->dev = &pdev->dev;
+
+ platform = of_get_child_by_name(pdev->dev.of_node, "platform");
+
+ if (platform) {
+ platform_dai_node = of_parse_phandle(platform, "sound-dai", 0);
+ of_node_put(platform);
+
+ if (!platform_dai_node) {
+ dev_err(&pdev->dev, "Failed to parse platform/sound-dai property\n");
+ return -EINVAL;
+ }
+ } else {
+ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->platforms->name)
+ continue;
+ dai_link->platforms->of_node = platform_dai_node;
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "%s snd_soc_register_card fail\n", __func__);
+ goto err_of_node_put;
+ }
+
+ return 0;
+
+err_of_node_put:
+ of_node_put(platform_dai_node);
+ return ret;
+}
+
+static const struct of_device_id an7581_i2s_machine_dt_match[] = {
+ { .compatible = "airoha,an7581-i2s" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, an7581_i2s_machine_dt_match);
+
+static struct platform_driver an7581_i2s_driver = {
+ .driver = {
+ .name = "an7581-i2s",
+ .of_match_table = an7581_i2s_machine_dt_match,
+ },
+ .probe = an7581_i2s_machine_probe,
+};
+module_platform_driver(an7581_i2s_driver);
+
+MODULE_DESCRIPTION("Airoha SoC I2S platform driver for ALSA AN7581");
+MODULE_LICENSE("GPL");
--- /dev/null
+++ b/sound/soc/airoha/an7581/an7581-reg.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * an7581-reg.h -- Airoha AN7581 audio driver reg definition
+ */
+
+#ifndef _AN7581_REG_H_
+#define _AN7581_REG_H_
+
+#define AFE_DAC_CON0 0x0
+
+#define AFE_DL1_BASE 0xa8
+#define AFE_DL1_END 0xac
+#define AFE_DL1_CUR 0xb0
+
+#define AFE_UL1_BASE 0xc4
+#define AFE_UL1_END 0xc8
+#define AFE_UL1_CUR 0xcc
+
+#define AFE_IRQ0_CON0 0xe4
+
+#define AFE_IRQ_STS 0xf8
+#define AFE_IRQ_STS_PLAY BIT(1)
+#define AFE_IRQ_STS_RECORD BIT(0)
+
+#define AFE_IRQ1_CON0 0x100
+
+#define AFE_MAX_REGISTER AFE_IRQ1_CON0
+
+#endif
@@ -0,0 +1,119 @@
From 3847173525e307ebcd23bd4863da943ea78b0057 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Tue, 20 Jan 2026 11:17:18 +0100
Subject: [PATCH] net: airoha: npu: Add the capability to read firmware names
from dts
Introduce the capability to read the firmware binary names from device-tree
using the firmware-name property if available.
This patch is needed because NPU firmware binaries are board specific since
they depend on the MediaTek WiFi chip used on the board (e.g. MT7996 or
MT7992) and the WiFi chip version info is not available in the NPU driver.
This is a preliminary patch to enable MT76 NPU offloading if the Airoha SoC
is equipped with MT7996 (Eagle) WiFi chipset.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20260120-airoha-npu-firmware-name-v4-2-88999628b4c1@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_npu.c | 46 ++++++++++++++++++++----
1 file changed, 40 insertions(+), 6 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_npu.c
+++ b/drivers/net/ethernet/airoha/airoha_npu.c
@@ -16,6 +16,8 @@
#define NPU_EN7581_FIRMWARE_DATA "airoha/en7581_npu_data.bin"
#define NPU_EN7581_FIRMWARE_RV32 "airoha/en7581_npu_rv32.bin"
+#define NPU_EN7581_7996_FIRMWARE_DATA "airoha/en7581_MT7996_npu_data.bin"
+#define NPU_EN7581_7996_FIRMWARE_RV32 "airoha/en7581_MT7996_npu_rv32.bin"
#define NPU_AN7583_FIRMWARE_DATA "airoha/an7583_npu_data.bin"
#define NPU_AN7583_FIRMWARE_RV32 "airoha/an7583_npu_rv32.bin"
#define NPU_EN7581_FIRMWARE_RV32_MAX_SIZE 0x200000
@@ -195,18 +197,18 @@ static int airoha_npu_send_msg(struct ai
}
static int airoha_npu_load_firmware(struct device *dev, void __iomem *addr,
- const struct airoha_npu_fw *fw_info)
+ const char *fw_name, int fw_max_size)
{
const struct firmware *fw;
int ret;
- ret = request_firmware(&fw, fw_info->name, dev);
+ ret = request_firmware(&fw, fw_name, dev);
if (ret)
return ret == -ENOENT ? -EPROBE_DEFER : ret;
- if (fw->size > fw_info->max_size) {
+ if (fw->size > fw_max_size) {
dev_err(dev, "%s: fw size too overlimit (%zu)\n",
- fw_info->name, fw->size);
+ fw_name, fw->size);
ret = -E2BIG;
goto out;
}
@@ -218,6 +220,28 @@ out:
return ret;
}
+static int
+airoha_npu_load_firmware_from_dts(struct device *dev, void __iomem *addr,
+ void __iomem *base)
+{
+ const char *fw_names[2];
+ int ret;
+
+ ret = of_property_read_string_array(dev->of_node, "firmware-name",
+ fw_names, ARRAY_SIZE(fw_names));
+ if (ret != ARRAY_SIZE(fw_names))
+ return -EINVAL;
+
+ ret = airoha_npu_load_firmware(dev, addr, fw_names[0],
+ NPU_EN7581_FIRMWARE_RV32_MAX_SIZE);
+ if (ret)
+ return ret;
+
+ return airoha_npu_load_firmware(dev, base + REG_NPU_LOCAL_SRAM,
+ fw_names[1],
+ NPU_EN7581_FIRMWARE_DATA_MAX_SIZE);
+}
+
static int airoha_npu_run_firmware(struct device *dev, void __iomem *base,
struct reserved_mem *rmem)
{
@@ -233,14 +257,22 @@ static int airoha_npu_run_firmware(struc
if (IS_ERR(addr))
return PTR_ERR(addr);
+ /* Try to load firmware images using the firmware names provided via
+ * dts if available.
+ */
+ if (of_find_property(dev->of_node, "firmware-name", NULL))
+ return airoha_npu_load_firmware_from_dts(dev, addr, base);
+
/* Load rv32 npu firmware */
- ret = airoha_npu_load_firmware(dev, addr, &soc->fw_rv32);
+ ret = airoha_npu_load_firmware(dev, addr, soc->fw_rv32.name,
+ soc->fw_rv32.max_size);
if (ret)
return ret;
/* Load data npu firmware */
return airoha_npu_load_firmware(dev, base + REG_NPU_LOCAL_SRAM,
- &soc->fw_data);
+ soc->fw_data.name,
+ soc->fw_data.max_size);
}
static irqreturn_t airoha_npu_mbox_handler(int irq, void *npu_instance)
@@ -797,6 +829,8 @@ module_platform_driver(airoha_npu_driver
MODULE_FIRMWARE(NPU_EN7581_FIRMWARE_DATA);
MODULE_FIRMWARE(NPU_EN7581_FIRMWARE_RV32);
+MODULE_FIRMWARE(NPU_EN7581_7996_FIRMWARE_DATA);
+MODULE_FIRMWARE(NPU_EN7581_7996_FIRMWARE_RV32);
MODULE_FIRMWARE(NPU_AN7583_FIRMWARE_DATA);
MODULE_FIRMWARE(NPU_AN7583_FIRMWARE_RV32);
MODULE_LICENSE("GPL");
@@ -0,0 +1,53 @@
From aebf15e8eb09b01e99f043e9f5d423798aac9d32 Mon Sep 17 00:00:00 2001
From: Zhengping Zhang <aquapinn@qq.com>
Date: Thu, 26 Feb 2026 10:37:08 +0800
Subject: [PATCH] net: airoha: fix typo in function name
Corrected the typo in the function name from
`airhoa_is_lan_gdm_port` to `airoha_is_lan_gdm_port`. This change ensures
consistency in the API naming convention.
Signed-off-by: Zhengping Zhang <aquapinn@qq.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/tencent_E4FD5D6BC0131E617D848896F5F9FCED6E0A@qq.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 2 +-
drivers/net/ethernet/airoha/airoha_eth.h | 2 +-
drivers/net/ethernet/airoha/airoha_ppe.c | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -76,7 +76,7 @@ static void airoha_set_macaddr(struct ai
struct airoha_eth *eth = port->qdma->eth;
u32 val, reg;
- reg = airhoa_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H
+ reg = airoha_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H
: REG_FE_WAN_MAC_H;
val = (addr[0] << 16) | (addr[1] << 8) | addr[2];
airoha_fe_wr(eth, reg, val);
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -626,7 +626,7 @@ u32 airoha_rmw(void __iomem *base, u32 o
#define airoha_qdma_clear(qdma, offset, val) \
airoha_rmw((qdma)->regs, (offset), (val), 0)
-static inline bool airhoa_is_lan_gdm_port(struct airoha_gdm_port *port)
+static inline bool airoha_is_lan_gdm_port(struct airoha_gdm_port *port)
{
/* GDM1 port on EN7581 SoC is connected to the lan dsa switch.
* GDM{2,3,4} can be used as wan port connected to an external
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -321,7 +321,7 @@ static int airoha_ppe_foe_entry_prepare(
/* For downlink traffic consume SRAM memory for hw
* forwarding descriptors queue.
*/
- if (airhoa_is_lan_gdm_port(port))
+ if (airoha_is_lan_gdm_port(port))
val |= AIROHA_FOE_IB2_FAST_PATH;
if (dsa_port >= 0)
val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ,
@@ -0,0 +1,44 @@
From 7600fb3b41dd6ab65ed61169df1b6099044edf97 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Wed, 4 Mar 2026 11:56:47 +0100
Subject: [PATCH] net: airoha: Rely __field_prep for non-constant masks
Rely on __field_prep macros for non-constant masks preparing the values
for register updates instead of open-coding.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260304-airoha-__field_prep-v1-1-b185facc4e2f@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1728,7 +1728,7 @@ static int airhoha_set_gdm2_loopback(str
airoha_fe_rmw(eth,
REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)),
SP_CPORT_MASK(val),
- FE_PSE_PORT_CDM2 << __ffs(SP_CPORT_MASK(val)));
+ __field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2));
if (port->id != AIROHA_GDM3_IDX && airoha_is_7581(eth))
airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6,
@@ -1782,7 +1782,7 @@ static int airoha_dev_init(struct net_de
ppe_id = pse_port == FE_PSE_PORT_PPE2 ? 1 : 0;
airoha_fe_rmw(eth, REG_PPE_DFT_CPORT0(ppe_id),
DFT_CPORT_MASK(port->id),
- fe_cpu_port << __ffs(DFT_CPORT_MASK(port->id)));
+ __field_prep(DFT_CPORT_MASK(port->id), fe_cpu_port));
return 0;
}
@@ -2139,7 +2139,7 @@ static int airoha_qdma_set_chan_tx_sched
airoha_qdma_rmw(port->qdma, REG_CHAN_QOS_MODE(channel >> 3),
CHAN_QOS_MODE_MASK(channel),
- mode << __ffs(CHAN_QOS_MODE_MASK(channel)));
+ __field_prep(CHAN_QOS_MODE_MASK(channel), mode));
return 0;
}
@@ -0,0 +1,52 @@
From bf3471e6e6c02137dc0d26caa783ac1849f9aab8 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Fri, 6 Mar 2026 09:07:27 +0100
Subject: [PATCH] net: airoha: Make flow control source port mapping dependent
on nbq parameter
Flow control source port mapping for USB serdes needs to be configured
according to the GDM port nbq parameter. This is a preliminary patch
since nbq parameter is specific for the given port serdes and needs to
be read from the DTS (in the current codebase is assigned statically).
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260306-airoha-fix-loopback-for-usb-serdes-v2-1-319de9c96826@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 10 ++++++----
drivers/net/ethernet/airoha/airoha_regs.h | 5 +----
2 files changed, 7 insertions(+), 8 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1730,10 +1730,12 @@ static int airhoha_set_gdm2_loopback(str
SP_CPORT_MASK(val),
__field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2));
- if (port->id != AIROHA_GDM3_IDX && airoha_is_7581(eth))
- airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6,
- FC_ID_OF_SRC_PORT24_MASK,
- FIELD_PREP(FC_ID_OF_SRC_PORT24_MASK, 2));
+ if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
+ u32 mask = FC_ID_OF_SRC_PORT_MASK(nbq);
+
+ airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask,
+ __field_prep(mask, AIROHA_GDM2_IDX));
+ }
return 0;
}
--- a/drivers/net/ethernet/airoha/airoha_regs.h
+++ b/drivers/net/ethernet/airoha/airoha_regs.h
@@ -376,10 +376,7 @@
#define SP_CPORT_MASK(_n) GENMASK(3 + ((_n) << 2), ((_n) << 2))
#define REG_SRC_PORT_FC_MAP6 0x2298
-#define FC_ID_OF_SRC_PORT27_MASK GENMASK(28, 24)
-#define FC_ID_OF_SRC_PORT26_MASK GENMASK(20, 16)
-#define FC_ID_OF_SRC_PORT25_MASK GENMASK(12, 8)
-#define FC_ID_OF_SRC_PORT24_MASK GENMASK(4, 0)
+#define FC_ID_OF_SRC_PORT_MASK(_n) GENMASK(4 + ((_n) << 3), ((_n) << 3))
#define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4
@@ -0,0 +1,86 @@
From 46097d011f77f5758fb47b7059b4f1f2e7403940 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Fri, 6 Mar 2026 16:09:47 +0100
Subject: [PATCH] net: airoha: Move GDM forward port configuration in
ndo_open/ndo_stop callbacks
This change allows to set GDM forward port configuration to
FE_PSE_PORT_DROP stopping the network device. Hw design requires to stop
packet forwarding putting the interface down. Moreover, PPE firmware
requires to use PPE1 for GDM3 or GDM4.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260306-airoha-gdm-forward-ndo-open-stop-v1-1-7b7a20dd9ef0@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1612,6 +1612,7 @@ static int airoha_dev_open(struct net_de
int err, len = ETH_HLEN + dev->mtu + ETH_FCS_LEN;
struct airoha_gdm_port *port = netdev_priv(dev);
struct airoha_qdma *qdma = port->qdma;
+ u32 pse_port = FE_PSE_PORT_PPE1;
netif_tx_start_all_queues(dev);
err = airoha_set_vip_for_gdm_port(port, true);
@@ -1635,6 +1636,14 @@ static int airoha_dev_open(struct net_de
GLOBAL_CFG_RX_DMA_EN_MASK);
atomic_inc(&qdma->users);
+ if (port->id == AIROHA_GDM2_IDX &&
+ airoha_ppe_is_enabled(qdma->eth, 1)) {
+ /* For PPE2 always use secondary cpu port. */
+ pse_port = FE_PSE_PORT_PPE2;
+ }
+ airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
+ pse_port);
+
return 0;
}
@@ -1652,6 +1661,9 @@ static int airoha_dev_stop(struct net_de
for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++)
netdev_tx_reset_subqueue(dev, i);
+ airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
+ FE_PSE_PORT_DROP);
+
if (atomic_dec_and_test(&qdma->users)) {
airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
GLOBAL_CFG_TX_DMA_EN_MASK |
@@ -1745,7 +1757,7 @@ static int airoha_dev_init(struct net_de
struct airoha_gdm_port *port = netdev_priv(dev);
struct airoha_qdma *qdma = port->qdma;
struct airoha_eth *eth = qdma->eth;
- u32 pse_port, fe_cpu_port;
+ u32 fe_cpu_port;
u8 ppe_id;
airoha_set_macaddr(port, dev->dev_addr);
@@ -1766,7 +1778,7 @@ static int airoha_dev_init(struct net_de
if (airoha_ppe_is_enabled(eth, 1)) {
/* For PPE2 always use secondary cpu port. */
fe_cpu_port = FE_PSE_PORT_CDM2;
- pse_port = FE_PSE_PORT_PPE2;
+ ppe_id = 1;
break;
}
fallthrough;
@@ -1775,13 +1787,11 @@ static int airoha_dev_init(struct net_de
/* For PPE1 select cpu port according to the running QDMA. */
fe_cpu_port = qdma_id ? FE_PSE_PORT_CDM2 : FE_PSE_PORT_CDM1;
- pse_port = FE_PSE_PORT_PPE1;
+ ppe_id = 0;
break;
}
}
- airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id), pse_port);
- ppe_id = pse_port == FE_PSE_PORT_PPE2 ? 1 : 0;
airoha_fe_rmw(eth, REG_PPE_DFT_CPORT0(ppe_id),
DFT_CPORT_MASK(port->id),
__field_prep(DFT_CPORT_MASK(port->id), fe_cpu_port));
@@ -0,0 +1,30 @@
From d4a533ad249e9fbdc2d0633f2ddd60a5b3a9a4ca Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Fri, 13 Mar 2026 12:27:00 +0100
Subject: [PATCH] net: airoha: Remove airoha_dev_stop() in airoha_remove()
Do not run airoha_dev_stop routine explicitly in airoha_remove()
since ndo_stop() callback is already executed by unregister_netdev() in
__dev_close_many routine if necessary and, doing so, we will end up causing
an underflow in the qdma users atomic counters. Rely on networking subsystem
to stop the device removing the airoha_eth module.
Fixes: 23020f0493270 ("net: airoha: Introduce ethernet support for EN7581 SoC")
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260313-airoha-remove-ndo_stop-remove-net-v2-1-67542c3ceeca@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 1 -
1 file changed, 1 deletion(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -3096,7 +3096,6 @@ static void airoha_remove(struct platfor
if (!port)
continue;
- airoha_dev_stop(port->dev);
unregister_netdev(port->dev);
airoha_metadata_dst_free(port);
}
@@ -0,0 +1,131 @@
From 8737d7194d6d5947c3d7d8813895b44a25b84477 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Fri, 13 Mar 2026 17:28:36 +0100
Subject: [PATCH] net: airoha: select QDMA block according LAN/WAN
configuration
Before this patch even GDM ports were assigned to QDMA0 while odd GDM
ports were using QDMA1, so, based on the DTS configuration, both QDMA0
and QDMA1 can theoretically receive traffic destinated to the host cpu
from LAN or WAN GDM ports.
Airoha folks reported the hw design assumes the LAN traffic destinated
to the host cpu is be forwarded to QDMA0 while traffic received on WAN
GDM port is managed by QDMA1. For this reason, select QDMA block according
to the GDM port LAN or WAN configuration:
- QDMA0 is used for GDM LAN devices
- QDMA1 is used for GDM WAN device
Assuming a device with three GDM ports, a typical configuration could be:
- MT7530 DSA switch -> GDM1 (eth0) -> QDMA0 (LAN traffic)
- External PHY -> GDM2 (eth1) -> QDMA1 (WAN traffic)
- External PHY -> GDM3 (eth2) -> QDMA0 (LAN traffic)
We can then bridge eth0 DSA port (lanX) with eth2 since they all tx/rx
LAN traffic.
Please note this patch introduces a change not visible to the user since
airoha_eth driver currently supports just the internal phy available via
the MT7530 DSA switch and there are no WAN interfaces officially supported
since PCS/external phy is not merged mainline yet (it will be posted with
following patches).
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260313-airoha-qdma-lan-wan-mode-v2-1-7d577db6e40c@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 18 ++++++++----------
drivers/net/ethernet/airoha/airoha_eth.h | 1 +
2 files changed, 9 insertions(+), 10 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1755,11 +1755,13 @@ static int airhoha_set_gdm2_loopback(str
static int airoha_dev_init(struct net_device *dev)
{
struct airoha_gdm_port *port = netdev_priv(dev);
- struct airoha_qdma *qdma = port->qdma;
- struct airoha_eth *eth = qdma->eth;
+ struct airoha_eth *eth = port->eth;
u32 fe_cpu_port;
u8 ppe_id;
+ /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
+ port->qdma = &eth->qdma[!airoha_is_lan_gdm_port(port)];
+ port->dev->irq = port->qdma->irq_banks[0].irq;
airoha_set_macaddr(port, dev->dev_addr);
switch (port->id) {
@@ -1783,7 +1785,7 @@ static int airoha_dev_init(struct net_de
}
fallthrough;
default: {
- u8 qdma_id = qdma - &eth->qdma[0];
+ u8 qdma_id = port->qdma - &eth->qdma[0];
/* For PPE1 select cpu port according to the running QDMA. */
fe_cpu_port = qdma_id ? FE_PSE_PORT_CDM2 : FE_PSE_PORT_CDM1;
@@ -2867,11 +2869,10 @@ bool airoha_is_valid_gdm_port(struct air
}
static int airoha_alloc_gdm_port(struct airoha_eth *eth,
- struct device_node *np, int index)
+ struct device_node *np)
{
const __be32 *id_ptr = of_get_property(np, "reg", NULL);
struct airoha_gdm_port *port;
- struct airoha_qdma *qdma;
struct net_device *dev;
int err, p;
u32 id;
@@ -2902,7 +2903,6 @@ static int airoha_alloc_gdm_port(struct
return -ENOMEM;
}
- qdma = &eth->qdma[index % AIROHA_MAX_NUM_QDMA];
dev->netdev_ops = &airoha_netdev_ops;
dev->ethtool_ops = &airoha_ethtool_ops;
dev->max_mtu = AIROHA_MAX_MTU;
@@ -2914,7 +2914,6 @@ static int airoha_alloc_gdm_port(struct
dev->features |= dev->hw_features;
dev->vlan_features = dev->hw_features;
dev->dev.of_node = np;
- dev->irq = qdma->irq_banks[0].irq;
SET_NETDEV_DEV(dev, eth->dev);
/* reserve hw queues for HTB offloading */
@@ -2935,7 +2934,7 @@ static int airoha_alloc_gdm_port(struct
port = netdev_priv(dev);
u64_stats_init(&port->stats.syncp);
spin_lock_init(&port->stats.lock);
- port->qdma = qdma;
+ port->eth = eth;
port->dev = dev;
port->id = id;
eth->ports[p] = port;
@@ -3035,7 +3034,6 @@ static int airoha_probe(struct platform_
for (i = 0; i < ARRAY_SIZE(eth->qdma); i++)
airoha_qdma_start_napi(&eth->qdma[i]);
- i = 0;
for_each_child_of_node(pdev->dev.of_node, np) {
if (!of_device_is_compatible(np, "airoha,eth-mac"))
continue;
@@ -3043,7 +3041,7 @@ static int airoha_probe(struct platform_
if (!of_device_is_available(np))
continue;
- err = airoha_alloc_gdm_port(eth, np, i++);
+ err = airoha_alloc_gdm_port(eth, np);
if (err) {
of_node_put(np);
goto error_napi_stop;
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -533,6 +533,7 @@ struct airoha_qdma {
struct airoha_gdm_port {
struct airoha_qdma *qdma;
+ struct airoha_eth *eth;
struct net_device *dev;
int id;
@@ -0,0 +1,32 @@
From dfdf774656205515b2d6ad94fce63c7ccbe92d91 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Fri, 9 Jan 2026 10:29:06 +0100
Subject: [PATCH] net: airoha: Fix typo in airoha_ppe_setup_tc_block_cb
definition
Fix Typo in airoha_ppe_dev_setup_tc_block_cb routine definition when
CONFIG_NET_AIROHA is not enabled.
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202601090517.Fj6v501r-lkp@intel.com/
Fixes: f45fc18b6de04 ("net: airoha: Add airoha_ppe_dev struct definition")
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260109-airoha_ppe_dev_setup_tc_block_cb-typo-v1-1-282e8834a9f9@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
include/linux/soc/airoha/airoha_offload.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- a/include/linux/soc/airoha/airoha_offload.h
+++ b/include/linux/soc/airoha/airoha_offload.h
@@ -51,8 +51,8 @@ static inline void airoha_ppe_put_dev(st
{
}
-static inline int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev,
- void *type_data)
+static inline int airoha_ppe_dev_setup_tc_block_cb(struct airoha_ppe_dev *dev,
+ void *type_data)
{
return -EOPNOTSUPP;
}
@@ -19,16 +19,16 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
FIELD_PREP(IP_ASSEMBLE_PORT_MASK, 0) |
FIELD_PREP(IP_ASSEMBLE_NBQ_MASK, 22));
- airoha_fe_set(eth, REG_GDM_FWD_CFG(3), GDM_PAD_EN_MASK);
- airoha_fe_set(eth, REG_GDM_FWD_CFG(4), GDM_PAD_EN_MASK);
+ airoha_fe_set(eth, REG_GDM_FWD_CFG(3),
- airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM3_IDX), GDM_PAD_EN_MASK);
- airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM4_IDX), GDM_PAD_EN_MASK);
+ airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM3_IDX),
+ GDM_PAD_EN_MASK | GDM_STRIP_CRC_MASK);
+ airoha_fe_set(eth, REG_GDM_FWD_CFG(4),
+ airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM4_IDX),
+ GDM_PAD_EN_MASK | GDM_STRIP_CRC_MASK);
airoha_fe_crsn_qsel_init(eth);
@@ -1625,7 +1627,8 @@ static int airoha_dev_open(struct net_de
@@ -1626,7 +1628,8 @@ static int airoha_dev_open(struct net_de
if (err)
return err;
@@ -15,7 +15,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -3089,7 +3089,6 @@ static void airoha_remove(struct platfor
@@ -3114,7 +3114,6 @@ static void airoha_remove(struct platfor
}
static const char * const en7581_xsi_rsts_names[] = {
@@ -23,7 +23,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
"hsi0-mac",
"hsi1-mac",
"hsi-mac",
@@ -3121,7 +3120,6 @@ static int airoha_en7581_get_src_port_id
@@ -3146,7 +3145,6 @@ static int airoha_en7581_get_src_port_id
}
static const char * const an7583_xsi_rsts_names[] = {
@@ -35,9 +35,9 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr)
{
struct airoha_eth *eth = port->qdma->eth;
@@ -1622,6 +1628,17 @@ static int airoha_dev_open(struct net_de
struct airoha_gdm_port *port = netdev_priv(dev);
@@ -1623,6 +1629,17 @@ static int airoha_dev_open(struct net_de
struct airoha_qdma *qdma = port->qdma;
u32 pse_port = FE_PSE_PORT_PPE1;
+ if (airhoa_is_phy_external(port)) {
+ err = phylink_of_phy_connect(port->phylink, dev->dev.of_node, 0);
@@ -53,7 +53,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
netif_tx_start_all_queues(dev);
err = airoha_set_vip_for_gdm_port(port, true);
if (err)
@@ -1675,6 +1692,11 @@ static int airoha_dev_stop(struct net_de
@@ -1687,6 +1704,11 @@ static int airoha_dev_stop(struct net_de
}
}
@@ -65,7 +65,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
return 0;
}
@@ -2817,6 +2839,20 @@ static const struct ethtool_ops airoha_e
@@ -2834,6 +2856,20 @@ static const struct ethtool_ops airoha_e
.get_link = ethtool_op_get_link,
};
@@ -86,7 +86,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
static int airoha_metadata_dst_alloc(struct airoha_gdm_port *port)
{
int i;
@@ -2861,6 +2897,99 @@ bool airoha_is_valid_gdm_port(struct air
@@ -2878,6 +2914,99 @@ bool airoha_is_valid_gdm_port(struct air
return false;
}
@@ -184,34 +184,34 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+}
+
static int airoha_alloc_gdm_port(struct airoha_eth *eth,
struct device_node *np, int index)
struct device_node *np)
{
@@ -2939,6 +3068,12 @@ static int airoha_alloc_gdm_port(struct
if (err)
return err;
@@ -2949,6 +3078,12 @@ static int airoha_alloc_gdm_port(struct
port->id = id;
eth->ports[p] = port;
+ if (airhoa_is_phy_external(port)) {
+ err = airoha_setup_phylink(dev);
+ if (err)
+ goto free_metadata_dst;
+ return err;
+ }
+
err = register_netdev(dev);
if (err)
goto free_metadata_dst;
@@ -3054,6 +3189,10 @@ error_hw_cleanup:
if (port && port->dev->reg_state == NETREG_REGISTERED) {
return airoha_metadata_dst_alloc(port);
}
@@ -3080,6 +3215,10 @@ error_hw_cleanup:
if (port->dev->reg_state == NETREG_REGISTERED)
unregister_netdev(port->dev);
airoha_metadata_dst_free(port);
+ if (airhoa_is_phy_external(port)) {
+ phylink_destroy(port->phylink);
+ airoha_pcs_destroy(port->pcs);
+ }
}
+ if (airhoa_is_phy_external(port)) {
+ phylink_destroy(port->phylink);
+ airoha_pcs_destroy(port->pcs);
+ }
airoha_metadata_dst_free(port);
}
free_netdev(eth->napi_dev);
@@ -3081,6 +3220,10 @@ static void airoha_remove(struct platfor
airoha_dev_stop(port->dev);
@@ -3106,6 +3245,10 @@ static void airoha_remove(struct platfor
unregister_netdev(port->dev);
airoha_metadata_dst_free(port);
+ if (airhoa_is_phy_external(port)) {
@@ -223,7 +223,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -536,6 +536,10 @@ struct airoha_gdm_port {
@@ -537,6 +537,10 @@ struct airoha_gdm_port {
struct net_device *dev;
int id;
@@ -0,0 +1,145 @@
From 843e2892f2d9353bf039e0dfb5442a600e75009e Mon Sep 17 00:00:00 2001
From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
Date: Thu, 9 Oct 2025 23:46:08 +0300
Subject: [PATCH] net: airoha: disable external phy code if PCS_AIROHA is not
enabled
External phy code breaks building for EN7523, so disable it if
PCS_AIROHA is not selected.
Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
---
drivers/net/ethernet/airoha/airoha_eth.c | 16 ++++++++++++++++
drivers/net/ethernet/airoha/airoha_eth.h | 2 ++
2 files changed, 18 insertions(+)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -72,10 +72,12 @@ static void airoha_qdma_irq_disable(stru
airoha_qdma_set_irqmask(irq_bank, index, mask, 0);
}
+#if defined(CONFIG_PCS_AIROHA)
static bool airhoa_is_phy_external(struct airoha_gdm_port *port)
{
return port->id != 1;
}
+#endif
static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr)
{
@@ -1632,6 +1634,7 @@ static int airoha_dev_open(struct net_de
struct airoha_qdma *qdma = port->qdma;
u32 pse_port = FE_PSE_PORT_PPE1;
+#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
err = phylink_of_phy_connect(port->phylink, dev->dev.of_node, 0);
if (err) {
@@ -1642,6 +1645,7 @@ static int airoha_dev_open(struct net_de
phylink_start(port->phylink);
}
+#endif
netif_tx_start_all_queues(dev);
err = airoha_set_vip_for_gdm_port(port, true);
@@ -1707,10 +1711,12 @@ static int airoha_dev_stop(struct net_de
}
}
+#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
phylink_stop(port->phylink);
phylink_disconnect_phy(port->phylink);
}
+#endif
return 0;
}
@@ -2859,6 +2865,7 @@ static const struct ethtool_ops airoha_e
.get_link = ethtool_op_get_link,
};
+#if defined(CONFIG_PCS_AIROHA)
static struct phylink_pcs *airoha_phylink_mac_select_pcs(struct phylink_config *config,
phy_interface_t interface)
{
@@ -2872,6 +2879,7 @@ static void airoha_mac_config(struct phy
const struct phylink_link_state *state)
{
}
+#endif
static int airoha_metadata_dst_alloc(struct airoha_gdm_port *port)
{
@@ -2917,6 +2925,7 @@ bool airoha_is_valid_gdm_port(struct air
return false;
}
+#if defined(CONFIG_PCS_AIROHA)
static void airoha_mac_link_up(struct phylink_config *config, struct phy_device *phy,
unsigned int mode, phy_interface_t interface,
int speed, int duplex, bool tx_pause, bool rx_pause)
@@ -3009,6 +3018,7 @@ static int airoha_setup_phylink(struct n
return 0;
}
+#endif
static int airoha_alloc_gdm_port(struct airoha_eth *eth,
struct device_node *np)
@@ -3081,11 +3091,13 @@ static int airoha_alloc_gdm_port(struct
port->id = id;
eth->ports[p] = port;
+#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
err = airoha_setup_phylink(dev);
if (err)
return err;
}
+#endif
return airoha_metadata_dst_alloc(port);
}
@@ -3218,10 +3230,12 @@ error_hw_cleanup:
if (port->dev->reg_state == NETREG_REGISTERED)
unregister_netdev(port->dev);
+#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
phylink_destroy(port->phylink);
airoha_pcs_destroy(port->pcs);
}
+#endif
airoha_metadata_dst_free(port);
}
free_netdev(eth->napi_dev);
@@ -3248,10 +3262,12 @@ static void airoha_remove(struct platfor
unregister_netdev(port->dev);
airoha_metadata_dst_free(port);
+#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
phylink_destroy(port->phylink);
airoha_pcs_destroy(port->pcs);
}
+#endif
}
free_netdev(eth->napi_dev);
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -537,9 +537,11 @@ struct airoha_gdm_port {
struct net_device *dev;
int id;
+#if defined(CONFIG_PCS_AIROHA)
struct phylink *phylink;
struct phylink_config phylink_config;
struct phylink_pcs *pcs;
+#endif
struct airoha_hw_stats stats;
@@ -0,0 +1,42 @@
From: Ryan Chen <rchen14b@gmail.com>
Subject: clk: en7581: Separate PERST from refclk in PCIe clock callbacks
The EN7581 PCIe clock enable/disable callbacks currently toggle both
PERST (reset) and reference clock signals together. This prevents the
PCIe controller driver from properly sequencing PERST relative to MAC
register configuration, which is required for x2 link mode.
Separate the two concerns: clock callbacks only manage reference clocks
(REFCLK_EN0/EN1), while PERST (PERSTOUT/PERSTOUT1/PERSTOUT2) is left
to the PCIe controller driver to manage directly via the NP_SCU regmap.
Signed-off-by: Ryan Chen <rchen14b@gmail.com>
--- a/drivers/clk/clk-en7523.c
+++ b/drivers/clk/clk-en7523.c
@@ -909,9 +909,11 @@ static int en7581_pci_enable(struct clk_
struct regmap *map = cg->map;
u32 mask;
- mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 |
- REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 |
- REG_PCI_CONTROL_PERSTOUT;
+ /* Only enable reference clocks - PERST is managed separately by the
+ * PCIe controller driver to allow proper sequencing of MAC register
+ * configuration between PERST assert and deassert.
+ */
+ mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1;
regmap_set_bits(map, REG_PCI_CONTROL, mask);
return 0;
@@ -923,9 +925,8 @@ static void en7581_pci_disable(struct cl
struct regmap *map = cg->map;
u32 mask;
- mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 |
- REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 |
- REG_PCI_CONTROL_PERSTOUT;
+ /* Only disable reference clocks - PCIe driver manages PERST */
+ mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1;
regmap_clear_bits(map, REG_PCI_CONTROL, mask);
usleep_range(1000, 2000);
}
@@ -0,0 +1,244 @@
From: Ryan Chen <rchen14b@gmail.com>
Subject: PCI: mediatek-gen3: Add PCIe x2 link support for Airoha EN7581
The Airoha EN7581 SoC supports PCIe x2 mode where two PCIe lanes are
bonded together for a single x2 link. This requires coordination with
the NP_SCU system controller for serdes mux routing, PERST management,
and lane configuration.
The x2 initialization sequence:
1. Assert serdes reset and PERST before enabling clocks
2. Configure serdes mux for 2-lane mode
3. Enable reference clocks, deassert serdes reset
4. Clear x1 mode bit and write EQ presets on both MACs
5. Deassert PERST to start link training
After initial link training, if the link negotiates Gen2 instead of
Gen3, a serdes reset toggle forces the MAC to re-discover the PHY
Gen3 capability from the Link Capabilities 2 register, allowing the
link to retrain at Gen3 x2 (8 GT/s).
Signed-off-by: Ryan Chen <rchen14b@gmail.com>
--- a/drivers/pci/controller/pcie-mediatek-gen3.c
+++ b/drivers/pci/controller/pcie-mediatek-gen3.c
@@ -61,6 +61,14 @@
#define PCIE_LTSSM_STATE(val) ((val & PCIE_LTSSM_STATE_MASK) >> 24)
#define PCIE_LTSSM_STATE_L2_IDLE 0x14
+/* EN7581 x2 mode support */
+#define PCIE_SETTING_REG_X1_MODE BIT(13)
+
+/* EN7581 NP_SCU register offsets for x2 link init */
+#define NP_SCU_LANE_CFG0 0x830
+#define NP_SCU_LANE_CFG1 0x834
+#define NP_SCU_CTRL_REG 0x88
+
#define PCIE_LINK_STATUS_REG 0x154
#define PCIE_PORT_LINKUP BIT(8)
@@ -205,6 +213,11 @@ struct mtk_gen3_pcie {
DECLARE_BITMAP(msi_irq_in_use, PCIE_MSI_IRQS_NUM);
const struct mtk_gen3_pcie_pdata *soc;
+
+ /* EN7581 x2 mode support */
+ struct regmap *np_scu;
+ bool x2_mode;
+ void __iomem *sister_base;
};
/* LTSSM state in PCIE_LTSSM_STATUS_REG bit[28:24] */
@@ -932,6 +945,28 @@ static int mtk_pcie_en7581_power_up(stru
size = lower_32_bits(resource_size(entry->res));
regmap_write(pbus_regmap, args[1], GENMASK(31, __fls(size)));
+ /* Lookup NP_SCU regmap for x2 mode support */
+ pcie->np_scu = syscon_regmap_lookup_by_phandle(dev->of_node, "airoha,np-scu");
+ if (IS_ERR(pcie->np_scu)) {
+ dev_dbg(dev, "np_scu not available, x2 mode disabled\n");
+ pcie->np_scu = NULL;
+ }
+
+ /* Check for x2 mode property */
+ pcie->x2_mode = of_property_read_bool(dev->of_node, "airoha,x2-mode");
+ if (pcie->x2_mode)
+ dev_info(dev, "x2 mode enabled\n");
+
+ /* Map sister MAC for x2 mode (MAC1 at +0x20000) */
+ if (pcie->x2_mode) {
+ pcie->sister_base = devm_ioremap(dev,
+ pcie->reg_base + 0x20000, 0x20000);
+ if (!pcie->sister_base)
+ dev_warn(dev, "failed to map sister MAC for x2\n");
+ else
+ dev_info(dev, "x2 mode: sister MAC mapped\n");
+ }
+
err = phy_set_mode(pcie->phy, PHY_MODE_PCIE);
if (err) {
dev_err(dev, "failed to set PHY mode\n");
@@ -969,17 +1004,28 @@ static int mtk_pcie_en7581_power_up(stru
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
- val = FIELD_PREP(PCIE_VAL_LN0_DOWNSTREAM, 0x47) |
- FIELD_PREP(PCIE_VAL_LN1_DOWNSTREAM, 0x47) |
- FIELD_PREP(PCIE_VAL_LN0_UPSTREAM, 0x41) |
- FIELD_PREP(PCIE_VAL_LN1_UPSTREAM, 0x41);
- writel_relaxed(val, pcie->base + PCIE_EQ_PRESET_01_REG);
-
- val = PCIE_K_PHYPARAM_QUERY | PCIE_K_QUERY_TIMEOUT |
- FIELD_PREP(PCIE_K_PRESET_TO_USE_16G, 0x80) |
- FIELD_PREP(PCIE_K_PRESET_TO_USE, 0x2) |
- FIELD_PREP(PCIE_K_FINETUNE_MAX, 0xf);
- writel_relaxed(val, pcie->base + PCIE_PIPE4_PIE8_REG);
+ /*
+ * EN7581 x2: Assert PERST and serdes reset before enabling clocks
+ * so that MAC registers can be configured while devices are held
+ * in reset, ensuring link trains with the correct x2 settings.
+ */
+ if (pcie->x2_mode && pcie->np_scu) {
+ /* Assert serdes reset on all lanes */
+ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG1,
+ BIT(26) | BIT(27), BIT(26) | BIT(27));
+ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG0,
+ BIT(27), BIT(27));
+ msleep(100);
+
+ /* Assert PERST on all ports */
+ regmap_update_bits(pcie->np_scu, NP_SCU_CTRL_REG,
+ BIT(16) | BIT(26) | BIT(29), 0);
+
+ /* Set serdes mux for 2-lane mode (bits[1:0] = 2) */
+ regmap_update_bits(pcie->np_scu, NP_SCU_CTRL_REG,
+ BIT(0) | BIT(1), BIT(1));
+ mdelay(1);
+ }
err = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks);
if (err) {
@@ -988,12 +1034,121 @@ static int mtk_pcie_en7581_power_up(stru
}
/*
- * Airoha EN7581 performs PCIe reset via clk callbacks since it has a
- * hw issue with PCIE_PE_RSTB signal. Add wait for the time needed to
- * complete the PCIe reset.
+ * Airoha EN7581: clock enable only provides refclk (patch 911).
+ * For x2, PERST + serdes are already asserted above.
+ * Wait for refclk to stabilize.
*/
msleep(PCIE_T_PVPERL_MS);
+ if (pcie->x2_mode && pcie->np_scu) {
+ /*
+ * EN7581 x2: PERST asserted, serdes reset asserted,
+ * refclk now stable. Complete the init sequence.
+ */
+ msleep(30);
+
+ /* Deassert serdes reset on all lanes */
+ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG1,
+ BIT(26) | BIT(27), 0);
+ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG0,
+ BIT(27), 0);
+
+ /* Clear SETTING_REG bit 13 for x2 mode on both MACs */
+ val = readl_relaxed(pcie->base + PCIE_SETTING_REG);
+ writel_relaxed(val & ~PCIE_SETTING_REG_X1_MODE,
+ pcie->base + PCIE_SETTING_REG);
+ if (pcie->sister_base) {
+ val = readl_relaxed(pcie->sister_base + 0x80);
+ writel_relaxed(val & ~PCIE_SETTING_REG_X1_MODE,
+ pcie->sister_base + 0x80);
+ }
+
+ /* EQ presets on both MACs */
+ writel_relaxed(0x41474147, pcie->base + PCIE_EQ_PRESET_01_REG);
+ if (pcie->sister_base)
+ writel_relaxed(0x41474147, pcie->sister_base + 0x100);
+ writel_relaxed(0x1018020f, pcie->base + PCIE_PIPE4_PIE8_REG);
+ if (pcie->sister_base)
+ writel_relaxed(0x1018020f, pcie->sister_base + 0x338);
+
+ /* Deassert PERST for all ports - link training starts */
+ msleep(10);
+ regmap_update_bits(pcie->np_scu, NP_SCU_CTRL_REG,
+ BIT(16) | BIT(26) | BIT(29),
+ BIT(16) | BIT(26) | BIT(29));
+
+ /* Wait for link training to complete */
+ msleep(800);
+
+ /*
+ * Check if link trained at Gen3. If not, toggle serdes
+ * reset to force MAC to re-discover PHY Gen3 capability.
+ * Without this, MAC only advertises Gen1-Gen2 in LnkCap2.
+ */
+ val = readl_relaxed(pcie->base + PCIE_LINK_STATUS_REG);
+ if (val & PCIE_PORT_LINKUP) {
+ void __iomem *cfg = pcie->base + PCIE_CFG_OFFSET_ADDR;
+ u8 cap_ptr;
+ int speed = 0;
+
+ /* Walk PCI cap list to find PCIe cap (ID=0x10) */
+ cap_ptr = readl_relaxed(cfg + PCI_CAPABILITY_LIST) & 0xFF;
+ while (cap_ptr >= 0x40) {
+ u32 hdr = readl_relaxed(cfg + cap_ptr);
+ if ((hdr & 0xFF) == PCI_CAP_ID_EXP) {
+ u32 lnk = readl_relaxed(cfg + cap_ptr + PCI_EXP_LNKCTL);
+ speed = (lnk >> 16) & PCI_EXP_LNKSTA_CLS;
+ break;
+ }
+ cap_ptr = (hdr >> 8) & 0xFF;
+ }
+
+ if (speed > 0 && speed < 3) {
+ dev_info(dev, "x2: link at Gen%d, toggling serdes for Gen3\n", speed);
+ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG0,
+ BIT(7) | BIT(8), BIT(7) | BIT(8));
+ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG1,
+ BIT(26) | BIT(27), BIT(26) | BIT(27));
+ msleep(1000);
+ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG0,
+ BIT(7) | BIT(8), 0);
+ regmap_update_bits(pcie->np_scu, NP_SCU_LANE_CFG1,
+ BIT(26) | BIT(27), 0);
+ msleep(2000);
+ dev_info(dev, "x2: serdes toggle done, link retraining\n");
+ } else {
+ dev_info(dev, "x2: link at Gen%d, no toggle needed\n", speed);
+ }
+ } else {
+ dev_info(dev, "x2: link not up after init, skipping speed check\n");
+ }
+
+ dev_info(dev, "x2: init complete, PERST deasserted\n");
+ } else {
+ /*
+ * Non-x2 mode: standard EQ config then deassert PERST.
+ */
+ val = FIELD_PREP(PCIE_VAL_LN0_DOWNSTREAM, 0x47) |
+ FIELD_PREP(PCIE_VAL_LN1_DOWNSTREAM, 0x47) |
+ FIELD_PREP(PCIE_VAL_LN0_UPSTREAM, 0x41) |
+ FIELD_PREP(PCIE_VAL_LN1_UPSTREAM, 0x41);
+ writel_relaxed(val, pcie->base + PCIE_EQ_PRESET_01_REG);
+
+ val = PCIE_K_PHYPARAM_QUERY | PCIE_K_QUERY_TIMEOUT |
+ FIELD_PREP(PCIE_K_PRESET_TO_USE_16G, 0x80) |
+ FIELD_PREP(PCIE_K_PRESET_TO_USE, 0x2) |
+ FIELD_PREP(PCIE_K_FINETUNE_MAX, 0xf);
+ writel_relaxed(val, pcie->base + PCIE_PIPE4_PIE8_REG);
+
+ /* Deassert PERST for all ports */
+ if (pcie->np_scu) {
+ regmap_update_bits(pcie->np_scu, NP_SCU_CTRL_REG,
+ BIT(16) | BIT(26) | BIT(29),
+ BIT(16) | BIT(26) | BIT(29));
+ msleep(100);
+ }
+ }
+
return 0;
err_clk_prepare_enable:
@@ -0,0 +1,103 @@
From 2a6c045640c38a407a39cd40c3c4d8dd2fd89aa8 Mon Sep 17 00:00:00 2001
From: Geert Uytterhoeven <geert+renesas@glider.be>
Date: Thu, 6 Nov 2025 14:34:00 +0100
Subject: [PATCH 1/2] bitfield: Add less-checking __FIELD_{GET,PREP}()
The BUILD_BUG_ON_MSG() check against "~0ull" works only with "unsigned
(long) long" _mask types. For constant masks, that condition is usually
met, as GENMASK() yields an UL value. The few places where the
constant mask is stored in an intermediate variable were fixed by
changing the variable type to u64 (see e.g. [1] and [2]).
However, for non-constant masks, smaller unsigned types should be valid,
too, but currently lead to "result of comparison of constant
18446744073709551615 with expression of type ... is always
false"-warnings with clang and W=1.
Hence refactor the __BF_FIELD_CHECK() helper, and factor out
__FIELD_{GET,PREP}(). The later lack the single problematic check, but
are otherwise identical to FIELD_{GET,PREP}(), and are intended to be
used in the fully non-const variants later.
[1] commit 5c667d5a5a3ec166 ("clk: sp7021: Adjust width of _m in
HWM_FIELD_PREP()")
[2] commit cfd6fb45cfaf46fa ("crypto: ccree - avoid out-of-range
warnings from clang")
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://git.kernel.org/torvalds/c/5c667d5a5a3ec166 [1]
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
---
include/linux/bitfield.h | 36 ++++++++++++++++++++++++++++--------
1 file changed, 28 insertions(+), 8 deletions(-)
--- a/include/linux/bitfield.h
+++ b/include/linux/bitfield.h
@@ -60,7 +60,7 @@
#define __bf_cast_unsigned(type, x) ((__unsigned_scalar_typeof(type))(x))
-#define __BF_FIELD_CHECK(_mask, _reg, _val, _pfx) \
+#define __BF_FIELD_CHECK_MASK(_mask, _val, _pfx) \
({ \
BUILD_BUG_ON_MSG(!__builtin_constant_p(_mask), \
_pfx "mask is not constant"); \
@@ -68,13 +68,33 @@
BUILD_BUG_ON_MSG(__builtin_constant_p(_val) ? \
~((_mask) >> __bf_shf(_mask)) & (_val) : 0, \
_pfx "value too large for the field"); \
- BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \
- __bf_cast_unsigned(_reg, ~0ull), \
- _pfx "type of reg too small for mask"); \
__BUILD_BUG_ON_NOT_POWER_OF_2((_mask) + \
(1ULL << __bf_shf(_mask))); \
})
+#define __BF_FIELD_CHECK_REG(mask, reg, pfx) \
+ BUILD_BUG_ON_MSG(__bf_cast_unsigned(mask, mask) > \
+ __bf_cast_unsigned(reg, ~0ull), \
+ pfx "type of reg too small for mask")
+
+#define __BF_FIELD_CHECK(mask, reg, val, pfx) \
+ ({ \
+ __BF_FIELD_CHECK_MASK(mask, val, pfx); \
+ __BF_FIELD_CHECK_REG(mask, reg, pfx); \
+ })
+
+#define __FIELD_PREP(mask, val, pfx) \
+ ({ \
+ __BF_FIELD_CHECK_MASK(mask, val, pfx); \
+ ((typeof(mask))(val) << __bf_shf(mask)) & (mask); \
+ })
+
+#define __FIELD_GET(mask, reg, pfx) \
+ ({ \
+ __BF_FIELD_CHECK_MASK(mask, 0U, pfx); \
+ (typeof(mask))(((reg) & (mask)) >> __bf_shf(mask)); \
+ })
+
/**
* FIELD_MAX() - produce the maximum value representable by a field
* @_mask: shifted mask defining the field's length and position
@@ -111,8 +131,8 @@
*/
#define FIELD_PREP(_mask, _val) \
({ \
- __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
- ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \
+ __BF_FIELD_CHECK_REG(_mask, 0ULL, "FIELD_PREP: "); \
+ __FIELD_PREP(_mask, _val, "FIELD_PREP: "); \
})
#define __BF_CHECK_POW2(n) BUILD_BUG_ON_ZERO(((n) & ((n) - 1)) != 0)
@@ -151,8 +171,8 @@
*/
#define FIELD_GET(_mask, _reg) \
({ \
- __BF_FIELD_CHECK(_mask, _reg, 0U, "FIELD_GET: "); \
- (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
+ __BF_FIELD_CHECK_REG(_mask, _reg, "FIELD_GET: "); \
+ __FIELD_GET(_mask, _reg, "FIELD_GET: "); \
})
extern void __compiletime_error("value doesn't fit into mask")
@@ -0,0 +1,114 @@
From c1c6ab80b25c8db1e2ef5ae3ac8075d2c242ae13 Mon Sep 17 00:00:00 2001
From: Geert Uytterhoeven <geert+renesas@glider.be>
Date: Thu, 6 Nov 2025 14:34:01 +0100
Subject: [PATCH 2/2] bitfield: Add non-constant field_{prep,get}() helpers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The existing FIELD_{GET,PREP}() macros are limited to compile-time
constants. However, it is very common to prepare or extract bitfield
elements where the bitfield mask is not a compile-time constant.
To avoid this limitation, the AT91 clock driver and several other
drivers already have their own non-const field_{prep,get}() macros.
Make them available for general use by adding them to
<linux/bitfield.h>, and improve them slightly:
1. Avoid evaluating macro parameters more than once,
2. Replace "ffs() - 1" by "__ffs()",
3. Support 64-bit use on 32-bit architectures,
4. Wire field_{get,prep}() to FIELD_{GET,PREP}() when mask is
actually constant.
This is deliberately not merged into the existing FIELD_{GET,PREP}()
macros, as people expressed the desire to keep stricter variants for
increased safety, or for performance critical paths.
Yury: use __mask withing new macros.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Acked-by: Crt Mori <cmo@melexis.com>
Acked-by: Nuno Sá <nuno.sa@analog.com>
Acked-by: Richard Genoud <richard.genoud@bootlin.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Reviewed-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
---
include/linux/bitfield.h | 59 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)
--- a/include/linux/bitfield.h
+++ b/include/linux/bitfield.h
@@ -16,6 +16,7 @@
* FIELD_{GET,PREP} macros take as first parameter shifted mask
* from which they extract the base mask and shift amount.
* Mask must be a compilation time constant.
+ * field_{get,prep} are variants that take a non-const mask.
*
* Example:
*
@@ -222,4 +223,62 @@ __MAKE_OP(64)
#undef __MAKE_OP
#undef ____MAKE_OP
+#define __field_prep(mask, val) \
+ ({ \
+ __auto_type __mask = (mask); \
+ typeof(__mask) __val = (val); \
+ unsigned int __shift = BITS_PER_TYPE(__mask) <= 32 ? \
+ __ffs(__mask) : __ffs64(__mask); \
+ (__val << __shift) & __mask; \
+ })
+
+#define __field_get(mask, reg) \
+ ({ \
+ __auto_type __mask = (mask); \
+ typeof(__mask) __reg = (reg); \
+ unsigned int __shift = BITS_PER_TYPE(__mask) <= 32 ? \
+ __ffs(__mask) : __ffs64(__mask); \
+ (__reg & __mask) >> __shift; \
+ })
+
+/**
+ * field_prep() - prepare a bitfield element
+ * @mask: shifted mask defining the field's length and position, must be
+ * non-zero
+ * @val: value to put in the field
+ *
+ * Return: field value masked and shifted to its final destination
+ *
+ * field_prep() masks and shifts up the value. The result should be
+ * combined with other fields of the bitfield using logical OR.
+ * Unlike FIELD_PREP(), @mask is not limited to a compile-time constant.
+ * Typical usage patterns are a value stored in a table, or calculated by
+ * shifting a constant by a variable number of bits.
+ * If you want to ensure that @mask is a compile-time constant, please use
+ * FIELD_PREP() directly instead.
+ */
+#define field_prep(mask, val) \
+ (__builtin_constant_p(mask) ? __FIELD_PREP(mask, val, "field_prep: ") \
+ : __field_prep(mask, val))
+
+/**
+ * field_get() - extract a bitfield element
+ * @mask: shifted mask defining the field's length and position, must be
+ * non-zero
+ * @reg: value of entire bitfield
+ *
+ * Return: extracted field value
+ *
+ * field_get() extracts the field specified by @mask from the
+ * bitfield passed in as @reg by masking and shifting it down.
+ * Unlike FIELD_GET(), @mask is not limited to a compile-time constant.
+ * Typical usage patterns are a value stored in a table, or calculated by
+ * shifting a constant by a variable number of bits.
+ * If you want to ensure that @mask is a compile-time constant, please use
+ * FIELD_GET() directly instead.
+ */
+#define field_get(mask, reg) \
+ (__builtin_constant_p(mask) ? __FIELD_GET(mask, reg, "field_get: ") \
+ : __field_get(mask, reg))
+
#endif
@@ -0,0 +1,40 @@
From 0f8407a1f1c795c417e4c7750654a6024a3ec68b Mon Sep 17 00:00:00 2001
From: Geert Uytterhoeven <geert+renesas@glider.be>
Date: Thu, 6 Nov 2025 14:34:02 +0100
Subject: [PATCH] clk: at91: Convert to common field_{get,prep}() helpers
Drop the driver-specific field_get() and field_prep() macros, in favor
of the globally available variants from <linux/bitfield.h>.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Acked-by: Stephen Boyd <sboyd@kernel.org>
Acked-by: Claudiu Beznea <claudiu.beznea@tuxon.dev>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
---
drivers/clk/at91/clk-peripheral.c | 1 +
drivers/clk/at91/pmc.h | 5 -----
2 files changed, 1 insertion(+), 5 deletions(-)
--- a/drivers/clk/at91/clk-peripheral.c
+++ b/drivers/clk/at91/clk-peripheral.c
@@ -3,6 +3,7 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
+#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -114,9 +114,6 @@ struct at91_clk_pms {
unsigned int parent;
};
-#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
-#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
-
#define ndck(a, s) (a[s - 1].id + 1)
#define nck(a) (a[ARRAY_SIZE(a) - 1].id + 1)
struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
@@ -0,0 +1,37 @@
From 1fe1c28a108e4953f083c0106575ee0eccc296ae Mon Sep 17 00:00:00 2001
From: Geert Uytterhoeven <geert+renesas@glider.be>
Date: Thu, 6 Nov 2025 14:34:07 +0100
Subject: [PATCH] iio: mlx90614: Convert to common field_{get,prep}() helpers
Drop the driver-specific field_get() and field_prep() macros, in favor
of the globally available variants from <linux/bitfield.h>.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Acked-by: Crt Mori <cmo@melexis.com>
Signed-off-by: Yury Norov (NVIDIA) <yury.norov@gmail.com>
---
drivers/iio/temperature/mlx90614.c | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
--- a/drivers/iio/temperature/mlx90614.c
+++ b/drivers/iio/temperature/mlx90614.c
@@ -22,6 +22,7 @@
* the "wakeup" GPIO is not given, power management will be disabled.
*/
+#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
@@ -68,10 +69,6 @@
#define MLX90614_CONST_SCALE 20 /* Scale in milliKelvin (0.02 * 1000) */
#define MLX90614_CONST_FIR 0x7 /* Fixed value for FIR part of low pass filter */
-/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */
-#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
-#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
-
struct mlx_chip_info {
/* EEPROM offsets with 16-bit data, MSB first */
/* emissivity correction coefficient */