Merge Official Source

Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
This commit is contained in:
Tianling Shen
2026-07-06 10:21:31 +08:00
91 changed files with 1316 additions and 638 deletions
@@ -0,0 +1,61 @@
From 52bd8e248c4b4766037f30a5a93a9bcf59abd2c6 Mon Sep 17 00:00:00 2001
From: Shiji Yang <yangshiji66@outlook.com>
Date: Fri, 3 Jul 2026 21:56:16 +0800
Subject: [PATCH] increase max SPL size
The GCC 14.4 generates slightly larger binaries for the Cortex-A5
platform. This resulted in the build failure on some emmc based
boards. Fix it by increasing the CONFIG_SPL_MAX_SIZE.
Signed-off-by: Shiji Yang <yangshiji66@outlook.com>
---
include/configs/sama5d27_som1_ek.h | 2 +-
include/configs/sama5d27_wlsom1_ek.h | 2 +-
include/configs/sama5d2_icp.h | 2 +-
include/configs/sama5d2_xplained.h | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
--- a/include/configs/sama5d27_som1_ek.h
+++ b/include/configs/sama5d27_som1_ek.h
@@ -31,7 +31,7 @@
#endif
/* SPL */
-#define CONFIG_SPL_MAX_SIZE 0x10000
+#define CONFIG_SPL_MAX_SIZE 0x18000
#define CONFIG_SPL_BSS_START_ADDR 0x20000000
#define CONFIG_SPL_BSS_MAX_SIZE 0x80000
#define CONFIG_SYS_SPL_MALLOC_START 0x20080000
--- a/include/configs/sama5d27_wlsom1_ek.h
+++ b/include/configs/sama5d27_wlsom1_ek.h
@@ -27,7 +27,7 @@
#endif
/* SPL */
-#define CONFIG_SPL_MAX_SIZE 0x10000
+#define CONFIG_SPL_MAX_SIZE 0x18000
#define CONFIG_SPL_BSS_START_ADDR 0x20000000
#define CONFIG_SPL_BSS_MAX_SIZE 0x80000
#define CONFIG_SYS_SPL_MALLOC_START 0x20080000
--- a/include/configs/sama5d2_icp.h
+++ b/include/configs/sama5d2_icp.h
@@ -43,7 +43,7 @@
#endif
/* SPL */
-#define CONFIG_SPL_MAX_SIZE 0x10000
+#define CONFIG_SPL_MAX_SIZE 0x18000
#define CONFIG_SPL_BSS_START_ADDR 0x20000000
#define CONFIG_SPL_BSS_MAX_SIZE 0x80000
#define CONFIG_SYS_SPL_MALLOC_START 0x20080000
--- a/include/configs/sama5d2_xplained.h
+++ b/include/configs/sama5d2_xplained.h
@@ -52,7 +52,7 @@
#endif
/* SPL */
-#define CONFIG_SPL_MAX_SIZE 0x10000
+#define CONFIG_SPL_MAX_SIZE 0x18000
#define CONFIG_SPL_BSS_START_ADDR 0x20000000
#define CONFIG_SPL_BSS_MAX_SIZE 0x80000
#define CONFIG_SYS_SPL_MALLOC_START 0x20080000
@@ -39,6 +39,7 @@ endef
define Build/Compile
IUCODE_TOOL=$(STAGING_DIR)/../host/bin/iucode_tool $(MAKE) -C $(PKG_BUILD_DIR)
rm -rf $(PKG_BUILD_DIR)/intel-ucode-ipkg
mkdir -p $(PKG_BUILD_DIR)/intel-ucode-ipkg
$(STAGING_DIR)/../host/bin/iucode_tool -q \
--write-firmware=$(PKG_BUILD_DIR)/intel-ucode-ipkg $(PKG_BUILD_DIR)/$(MICROCODE).bin
@@ -441,7 +441,7 @@ static int gpio_keys_button_probe(struct platform_device *pdev,
struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
struct gpio_keys_button_dev *bdev;
struct gpio_keys_button *buttons;
struct device_node *prev = NULL;
struct fwnode_handle *prev = NULL;
int error = 0;
int i;
@@ -522,11 +522,11 @@ static int gpio_keys_button_probe(struct platform_device *pdev,
}
} else {
/* Device-tree */
struct device_node *child =
of_get_next_child(dev->of_node, prev);
struct fwnode_handle *child =
device_get_next_child_node(dev, prev);
bdata->gpiod = devm_fwnode_gpiod_get(dev,
of_fwnode_handle(child), NULL, GPIOD_IN,
child, NULL, GPIOD_IN,
desc);
prev = child;
@@ -579,7 +579,7 @@ static int gpio_keys_button_probe(struct platform_device *pdev,
error = 0;
out:
of_node_put(prev);
fwnode_handle_put(prev);
return error;
}
@@ -102,7 +102,7 @@
int ret;
if (!of_device_is_available(np))
@@ -633,6 +714,43 @@ static int ath9k_of_init(struct ath_soft
@@ -633,6 +714,46 @@ static int ath9k_of_init(struct ath_soft
ath_dbg(common, CONFIG, "parsing configuration from OF node\n");
@@ -115,11 +115,13 @@
+#ifdef CONFIG_ATH79
+ if (ah->hw_version.devid == AR5416_AR9100_DEVID) {
+ ah->external_reset = ar913x_wmac_reset;
+ ah->external_reset();
+ } else if (ah->hw_version.devid == AR9300_DEVID_AR9330) {
+ ah->get_mac_revision = ar9330_get_soc_revision;
+ u32 t = ath79_reset_rr(AR933X_RESET_REG_BOOTSTRAP);
+ ah->is_clk_25mhz = !(t & AR933X_BOOTSTRAP_REF_CLK_40);
+ ah->external_reset = ar933x_wmac_reset;
+ ah->external_reset();
+ } else if (ah->hw_version.devid == AR9300_DEVID_AR9340) {
+ ah->get_mac_revision = ath79_get_soc_revision;
+ u32 t = ath79_reset_rr(AR934X_RESET_REG_BOOTSTRAP);
@@ -136,6 +138,7 @@
+ u32 t = ath79_reset_rr(QCA955X_RESET_REG_BOOTSTRAP);
+ ah->is_clk_25mhz = !(t & QCA955X_BOOTSTRAP_REF_CLK_40);
+ ah->external_reset = qca955x_wmac_reset;
+ ah->external_reset();
+ } else if (ah->hw_version.devid == AR9300_DEVID_QCA956X) {
+ ah->get_mac_revision = ath79_get_soc_revision;
+ u32 t = ath79_reset_rr(QCA956X_RESET_REG_BOOTSTRAP);
@@ -146,7 +149,7 @@
if (of_property_read_bool(np, "qca,no-eeprom")) {
/* ath9k-eeprom-<bus>-<id>.bin */
scnprintf(eeprom_name, sizeof(eeprom_name),
@@ -651,6 +769,17 @@ static int ath9k_of_init(struct ath_soft
@@ -651,6 +772,17 @@ static int ath9k_of_init(struct ath_soft
if (ret == -EPROBE_DEFER)
return ret;
@@ -144,15 +144,15 @@ UBNT_LEDBAR_CONTROL_RGBS(green);
UBNT_LEDBAR_CONTROL_RGBS(blue);
static int ubnt_ledbar_init_led(struct device_node *np, struct ubnt_ledbar *ledbar,
static int ubnt_ledbar_init_led(struct fwnode_handle *fw, struct ubnt_ledbar *ledbar,
struct led_classdev *led_cdev)
{
struct led_init_data init_data = {};
if (!np)
if (!fw)
return 0;
init_data.fwnode = of_fwnode_handle(np);
init_data.fwnode = fw;
led_cdev->max_brightness = UBNT_LEDBAR_MAX_BRIGHTNESS;
@@ -161,26 +161,24 @@ static int ubnt_ledbar_init_led(struct device_node *np, struct ubnt_ledbar *ledb
static int ubnt_ledbar_probe(struct i2c_client *client)
{
struct device_node *np = client->dev.of_node;
struct device *dev = &client->dev;
struct ubnt_ledbar *ledbar;
int err;
ledbar = devm_kzalloc(&client->dev, sizeof(*ledbar), GFP_KERNEL);
ledbar = devm_kzalloc(dev, sizeof(*ledbar), GFP_KERNEL);
if (!ledbar)
return -ENOMEM;
ledbar->enable_gpio = devm_gpiod_get(&client->dev, "enable", GPIOD_OUT_LOW);
ledbar->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(ledbar->enable_gpio))
return dev_err_probe(&client->dev, PTR_ERR(ledbar->enable_gpio), "Failed to get enable gpio");
ledbar->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW);
return dev_err_probe(dev, PTR_ERR(ledbar->enable_gpio), "Failed to get enable gpio");
ledbar->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ledbar->reset_gpio))
return dev_err_probe(&client->dev, PTR_ERR(ledbar->reset_gpio), "Failed to get reset gpio");
return dev_err_probe(dev, PTR_ERR(ledbar->reset_gpio), "Failed to get reset gpio");
ledbar->led_count = 1;
of_property_read_u32(np, "led-count", &ledbar->led_count);
device_property_read_u32(dev, "led-count", &ledbar->led_count);
ledbar->client = client;
@@ -194,13 +192,13 @@ static int ubnt_ledbar_probe(struct i2c_client *client)
ubnt_ledbar_reset(ledbar);
ledbar->led_red.brightness_set_blocking = ubnt_ledbar_set_red_brightness;
ubnt_ledbar_init_led(of_get_child_by_name(np, "red"), ledbar, &ledbar->led_red);
ubnt_ledbar_init_led(device_get_named_child_node(dev, "red"), ledbar, &ledbar->led_red);
ledbar->led_green.brightness_set_blocking = ubnt_ledbar_set_green_brightness;
ubnt_ledbar_init_led(of_get_child_by_name(np, "green"), ledbar, &ledbar->led_green);
ubnt_ledbar_init_led(device_get_named_child_node(dev, "green"), ledbar, &ledbar->led_green);
ledbar->led_blue.brightness_set_blocking = ubnt_ledbar_set_blue_brightness;
ubnt_ledbar_init_led(of_get_child_by_name(np, "blue"), ledbar, &ledbar->led_blue);
ubnt_ledbar_init_led(device_get_named_child_node(dev, "blue"), ledbar, &ledbar->led_blue);
return ubnt_ledbar_apply_state(ledbar);
}
+1 -1
View File
@@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=hostapd
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_SOURCE_URL:=https://w1.fi/hostap.git
PKG_SOURCE_PROTO:=git
@@ -0,0 +1,43 @@
From 46dd5a4ffc9bcf44cf8fc45120b3e1e5ec922187 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
Date: Tue, 31 Mar 2026 23:24:04 +0300
Subject: AP MLD: Fix link ID validation in Basic MLE parsing
Link ID 15 can be indicated in the field, but that is not a valid value
and must be rejected to avoid issues pointing beyond the array of links
for a non-AP MLD. Without this, an invalid MLE could result in writing
beyond the end of the buffer and causing process termination or
unexpected behavior.
Fixes: 5f5db9366cde ("AP: MLO: Process Multi-Link element from (Re)Association Request frame")
Signed-off-by: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
---
src/ap/ieee802_11_eht.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -1382,6 +1382,7 @@ u16 hostapd_process_ml_assoc_req(struct
u16 control;
const u8 *sub_elem_end;
int num_frag_subelems;
+ u8 link_id;
num_frag_subelems =
ieee802_11_defrag_mle_subelem(mlbuf, pos,
@@ -1433,8 +1434,13 @@ u16 hostapd_process_ml_assoc_req(struct
goto out;
}
control = WPA_GET_LE16(pos);
- link_info = &info->links[control &
- BASIC_MLE_STA_CTRL_LINK_ID_MASK];
+ link_id = control & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
+ if (link_id >= MAX_NUM_MLD_LINKS) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid Link ID in Per-STA Profile subelement");
+ goto out;
+ }
+ link_info = &info->links[link_id];
pos += 2;
if (!(control & BASIC_MLE_STA_CTRL_COMPLETE_PROFILE)) {
@@ -0,0 +1,38 @@
From aa9d345887389a251c63a3781d2ad2940d079193 Mon Sep 17 00:00:00 2001
From: Amarnath Hullur Subramanyam <amarnathhs@google.com>
Date: Thu, 30 Apr 2026 18:24:35 -0700
Subject: BSS: Add bounds check for link_id in Basic MLE parsing
In wpa_bss_parse_basic_ml_element() in bss.c, an extracted link_id is
used without validation against the maximum allowed links
(MAX_NUM_MLD_LINKS). Processing a malformed Basic Multi-Link element
(MLE) with an out-of-bounds link_id could lead to memory corruption.
However, the modified location is within the body of the received frame
and as such, this does not result in additional issues since that area
is controlled by the transmitter of the frame. In any case, it is better
to be explicit with validating the Link ID value.
This commit introduces a strict bounds check immediately after link_id
extraction. If link_id exceeds or equals MAX_NUM_MLD_LINKS, parsing is
gracefully aborted with a debug log entry.
Fixes: de5e01010cb2 ("wpa_supplicant: Support ML probe request")
Signed-off-by: Amarnath Hullur Subramanyam <amarnathhs@google.com>
---
wpa_supplicant/bss.c | 5 +++++
1 file changed, 5 insertions(+)
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -2055,6 +2055,11 @@ void wpa_bss_parse_basic_ml_element(stru
goto out;
link_id = ml_basic_common_info->variable[0] & EHT_ML_LINK_ID_MSK;
+ if (link_id >= MAX_NUM_MLD_LINKS) {
+ wpa_printf(MSG_DEBUG, "MLD: Invalid link ID %u in Basic MLE",
+ link_id);
+ goto out;
+ }
os_memcpy(bss->mld_addr, ml_basic_common_info->mld_addr, ETH_ALEN);
@@ -0,0 +1,45 @@
From a8531e3d871e6fa72f2f85d91e9f787326b2af8b Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
Date: Tue, 31 Mar 2026 23:16:08 +0300
Subject: MLD: Validate MLE Link ID fields in association rejection case
The Link ID Info field in the Common Info field needs to ignore the
reserved bits to be more extensible for future. Both that link ID for
the association link and the link IDs for other links need to be
verified to be within the valid range (0-14), so check that here. The
parsed link ID was not used for anything yet, but it is better to make
sure this in theory common parser is not exposing invalid data to the
caller should it be used for additional purposes in the future.
Signed-off-by: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
---
wpa_supplicant/events.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -4318,7 +4318,12 @@ static unsigned int wpas_ml_parse_assoc(
pos = common_info->variable;
/* Store the information for the association link */
- ml_info[i].link_id = *pos;
+ ml_info[i].link_id = *pos & EHT_ML_LINK_ID_MSK;
+ if (ml_info[i].link_id >= MAX_NUM_MLD_LINKS) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid Link ID value for assoc link");
+ goto out;
+ }
pos++;
/* Skip the BSS Parameters Change Count */
@@ -4474,6 +4479,10 @@ static unsigned int wpas_ml_parse_assoc(
MAC2STR(pos + 1), nstr_bitmap_len);
ml_info[i].link_id = ctrl & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
+ if (ml_info[i].link_id >= MAX_NUM_MLD_LINKS) {
+ wpa_printf(MSG_DEBUG, "MLD: Invalid Link ID value");
+ goto out;
+ }
os_memcpy(ml_info[i].bssid, pos + 1, ETH_ALEN);
pos += sta_info_len;
@@ -0,0 +1,38 @@
From 56216d113909650ae59621dc2dd16157afb94948 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
Date: Mon, 18 May 2026 11:57:02 +0300
Subject: Verify MLD link ID validity in get_basic_mle_link_id()
Link ID is 0..14, so ignore value 15 if an invalid frame is processed.
It does not look like the returned value was actually used to reference
any local array, but in any case, it is better to not return an invalid
value to reduce risk for unexpected behavior in the future.
Signed-off-by: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
---
src/common/ieee802_11_common.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -3714,6 +3714,7 @@ int get_basic_mle_link_id(const u8 *buf,
ETH_ALEN; /* MLD MAC Address field (Basic) */
size_t common_info_limit;
u8 common_info_len;
+ u8 link_id;
if (len < MULTI_LINK_CONTROL_LEN)
return -1;
@@ -3735,7 +3736,11 @@ int get_basic_mle_link_id(const u8 *buf,
if (link_id_pos + EHT_ML_LINK_ID_LEN > common_info_limit)
return -1;
- return buf[link_id_pos] & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
+ link_id = buf[link_id_pos] & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ return -1;
+
+ return link_id;
}
@@ -0,0 +1,49 @@
From e4bd3442c2223802bf8c4a4d868e3b9443c7caf4 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
Date: Mon, 18 May 2026 12:13:21 +0300
Subject: MLD: Verify link ID validity in MLE in reconfiguration cases
Ignore or reject invalid link ID value 15 in ML reconfiguration.
Signed-off-by: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
---
src/ap/ieee802_11_eht.c | 2 ++
wpa_supplicant/bss.c | 3 ++-
wpa_supplicant/wnm_sta.c | 3 ++-
3 files changed, 6 insertions(+), 2 deletions(-)
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -2166,6 +2166,8 @@ hostapd_parse_link_reconf_req_sta_profil
link_id = sta_control & EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK;
wpa_printf(MSG_DEBUG, "MLD: Per-STA profile for link=%u", link_id);
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ goto out;
reconf_type_mask =
sta_control & EHT_PER_STA_RECONF_CTRL_OP_UPDATE_TYPE_MSK;
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -2243,7 +2243,8 @@ u16 wpa_bss_parse_reconf_ml_element(stru
u8 link_id;
link_id = control & EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK;
- removed_links |= BIT(link_id);
+ if (link_id < MAX_NUM_MLD_LINKS)
+ removed_links |= BIT(link_id);
}
pos += 2 + sub_elem_len;
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -546,7 +546,8 @@ static void wnm_parse_neighbor_report_mu
control = le_to_host16(sta_prof->sta_control);
link_id = control & EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK;
- rep->mld_links |= BIT(link_id);
+ if (link_id < MAX_NUM_MLD_LINKS)
+ rep->mld_links |= BIT(link_id);
}
pos += 2 + sub_elem_len;
@@ -0,0 +1,37 @@
From ce1a8612e309fe86133ecf05ffb452b0bdf3b035 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
Date: Mon, 18 May 2026 15:45:15 +0300
Subject: AP MLD: Verify AP MLD link ID validity before updating bitmap of
links
Link ID is 0..14, so ignore value 15 if an invalid frame is processed.
It does not look like the invalid value was actually used to reference
any local array, but in any case, it is better to not mark an invalid
link as being specified.
Signed-off-by: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
---
src/ap/beacon.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -1411,6 +1411,7 @@ static bool parse_ml_probe_req(const str
for_each_element_id(sub, 0, pos, len) {
const struct ieee80211_eht_per_sta_profile *sta;
u16 sta_control;
+ u8 link_id;
if (*links == 0xffff)
*links = 0;
@@ -1430,7 +1431,9 @@ static bool parse_ml_probe_req(const str
* partial profile was requested.
*/
sta_control = le_to_host16(sta->sta_control);
- *links |= BIT(sta_control & BASIC_MLE_STA_CTRL_LINK_ID_MASK);
+ link_id = sta_control & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
+ if (link_id < MAX_NUM_MLD_LINKS)
+ *links |= BIT(link_id);
}
if (!for_each_element_completed(sub, pos, len)) {
@@ -0,0 +1,37 @@
From 41c86a2ebed50567c73de23c102c2bf83eb883f2 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
Date: Tue, 31 Mar 2026 17:47:03 +0300
Subject: MLD: Fix length check in common info for association failure cases
It is not sufficient to check that the indicated common info length is
sufficiently large to contain the information; there needs to be a check
for the indicated value to not be too large to go beyond the end of the
MLE as well. Without this, invalid MLE might result in ml_len wrapping
around to a huge value and reading beyond the end of the buffer for the
received frame. This could result in process termination.
Add the missed check for the Common Info field not being truncated in
the MLE in association failure cases.
Fixes: a58a0c592e20 ("MLD: Fix Multi-Link element parsing for association failures")
Signed-off-by: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
---
wpa_supplicant/events.c | 7 +++++++
1 file changed, 7 insertions(+)
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -4306,6 +4306,13 @@ static unsigned int wpas_ml_parse_assoc(
goto out;
}
+ if (sizeof(*ml) + common_info->len > ml_len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Truncated common info (common_info->len=%u ml_len=%zu)",
+ common_info->len, ml_len);
+ goto out;
+ }
+
wpa_printf(MSG_DEBUG, "MLD: address: " MACSTR,
MAC2STR(common_info->mld_addr));
@@ -0,0 +1,55 @@
From 595194d0305189922a057e8ea8b743a1bd8d2d29 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
Date: Mon, 18 May 2026 16:17:15 +0300
Subject: BSS: Fix validation of ML common info length during scan result
parsing
The validation of the ML common info length before use allowed reading
beyond the end of the element when the MSD info and EML capabilities
were claimed to be present but were not actually inclued. While this was
noticed after reading these fields and further processing of the element
was stopped, this could result in reading two bytes beyond the end of
the buffer.
Avoid this by checking the remaining length explicitly for the optiional
fields.
Fixes: 1b07e8baca13 ("BSS: Use correct AP MLD ID")
Signed-off-by: Jouni Malinen <jouni.malinen@oss.qualcomm.com>
---
wpa_supplicant/bss.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -2006,13 +2006,20 @@ void wpa_bss_parse_basic_ml_element(stru
/* Medium Synchronization Delay Information */
if (le_to_host16(eht_ml->ml_control) &
- BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO)
+ BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
+ if (ml_basic_common_info->len <
+ sizeof(*ml_basic_common_info) + pos + 2)
+ goto out;
pos += 2;
+ }
/* EML Capabilities */
bss->eml_capa = 0;
if (le_to_host16(eht_ml->ml_control) &
BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
+ if (ml_basic_common_info->len <
+ sizeof(*ml_basic_common_info) + pos + 2)
+ goto out;
bss->eml_capa =
WPA_GET_LE16(&ml_basic_common_info->variable[pos]);
pos += 2;
@@ -2020,6 +2027,8 @@ void wpa_bss_parse_basic_ml_element(stru
/* MLD Capabilities And Operations (always present, see
* control/control_mask) */
+ if (ml_basic_common_info->len < sizeof(*ml_basic_common_info) + pos + 2)
+ goto out;
bss->mld_capa = WPA_GET_LE16(&ml_basic_common_info->variable[pos]);
pos += 2;
@@ -278,7 +278,7 @@ This allows building both hostapd and wpa_supplicant as a single binary
os_memset(&eapol_test, 0, sizeof(eapol_test));
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -6627,8 +6627,8 @@ static int wpas_pasn_auth(struct wpa_sup
@@ -6643,8 +6643,8 @@ static int wpas_pasn_auth(struct wpa_sup
#endif /* CONFIG_PASN */
@@ -289,7 +289,7 @@ This allows building both hostapd and wpa_supplicant as a single binary
{
struct wpa_supplicant *wpa_s = ctx;
int resched;
@@ -7622,7 +7622,7 @@ void wpa_supplicant_event(void *ctx, enu
@@ -7638,7 +7638,7 @@ void wpa_supplicant_event(void *ctx, enu
}
@@ -53,7 +53,7 @@ probe/assoc/auth requests via object subscribe.
}
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -1464,6 +1464,12 @@ void handle_probe_req(struct hostapd_dat
@@ -1467,6 +1467,12 @@ void handle_probe_req(struct hostapd_dat
int mld_id;
u16 links;
#endif /* CONFIG_IEEE80211BE */
@@ -66,7 +66,7 @@ probe/assoc/auth requests via object subscribe.
if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
ssi_signal < hapd->iconf->rssi_ignore_probe_request)
@@ -1671,6 +1677,12 @@ void handle_probe_req(struct hostapd_dat
@@ -1674,6 +1680,12 @@ void handle_probe_req(struct hostapd_dat
}
#endif /* CONFIG_P2P */
@@ -1001,7 +1001,7 @@ as adding/removing interfaces.
MULTI_AP_FRONTHAUL_BSS);
wpa_s->multi_ap_ie = 1;
}
@@ -5857,6 +5864,13 @@ static void wpas_event_rx_mgmt_action(st
@@ -5873,6 +5880,13 @@ static void wpas_event_rx_mgmt_action(st
}
#endif /* CONFIG_WNM */
@@ -1015,7 +1015,7 @@ as adding/removing interfaces.
#ifdef CONFIG_GAS
if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC ||
mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) &&
@@ -6663,6 +6677,7 @@ void supplicant_event(void *ctx, enum wp
@@ -6679,6 +6693,7 @@ void supplicant_event(void *ctx, enum wp
event_to_string(event), event);
#endif /* CONFIG_NO_STDOUT_DEBUG */
@@ -36,7 +36,7 @@ full device, e.g. in order to deal with hardware/driver limitations
* ' ' (ascii 32): all environments
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -1701,7 +1701,7 @@ void handle_probe_req(struct hostapd_dat
@@ -1704,7 +1704,7 @@ void handle_probe_req(struct hostapd_dat
if (hapd->conf->no_probe_resp_if_max_sta &&
is_multicast_ether_addr(mgmt->da) &&
is_multicast_ether_addr(mgmt->bssid) &&
@@ -28,7 +28,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -1523,7 +1523,7 @@ void handle_probe_req(struct hostapd_dat
@@ -1526,7 +1526,7 @@ void handle_probe_req(struct hostapd_dat
* is less likely to see them (Probe Request frame sent on a
* neighboring, but partially overlapping, channel).
*/
+2 -2
View File
@@ -8,13 +8,13 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=nilfs-utils
PKG_VERSION:=2.3.0
PKG_VERSION:=2.3.1
PKG_RELEASE:=1
PKG_URL:=https://nilfs.sourceforge.io
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
PKG_SOURCE_URL:=https://nilfs.sourceforge.io/download/
PKG_HASH:=c0876a7ecd13d4b54cb65abb9ad6cd0bf1ed102d03ca12738f12eab9bd84e67b
PKG_HASH:=bf89d7ff4579c0df294b3493ed412cd4970b0a1eaaecb658e8f6c730153f0148
PKG_MAINTAINER:=Pavlo Samko <bulldozerbsg@gmail.com>
PKG_LICENSE:=GPL-2.0
+2 -1
View File
@@ -97,7 +97,8 @@ define Device/netgear_wndr4700
kmod-dm kmod-fs-ext4 kmod-fs-vfat kmod-usb-ledtrig-usbport \
kmod-md-mod kmod-nls-cp437 kmod-nls-iso8859-1 kmod-nls-iso8859-15 \
kmod-nls-utf8 kmod-usb-xhci-pci-renesas kmod-usb-dwc2 kmod-usb-storage \
partx-utils kmod-ata-dwc kmod-dsa-qca8k kmod-phy-qca83xx
partx-utils kmod-ata-dwc kmod-dsa-qca8k kmod-phy-qca83xx \
kmod-hwmon-tc654 kmod-hwmon-lm90 kmod-thermal
BOARD_NAME := wndr4700
PAGESIZE := 2048
SUBPAGESIZE := 512
-10
View File
@@ -1,7 +1,6 @@
CONFIG_BCH=y
CONFIG_CRYPTO_HASH_INFO=y
CONFIG_CRYPTO_ZSTD=y
CONFIG_HWMON=y
CONFIG_MTD_NAND_CORE=y
CONFIG_MTD_NAND_ECC=y
CONFIG_MTD_NAND_ECC_SW_BCH=y
@@ -21,17 +20,8 @@ CONFIG_RTC_I2C_AND_SPI=y
CONFIG_RTC_MC146818_LIB=y
CONFIG_RTL8366_SMI=y
CONFIG_RTL8367B_PHY=y
CONFIG_SENSORS_GPIO_FAN=y
CONFIG_SENSORS_LM90=y
CONFIG_SENSORS_TC654=y
CONFIG_SWCONFIG=y
CONFIG_SWCONFIG_LEDS=y
CONFIG_THERMAL=y
CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
CONFIG_THERMAL_GOV_STEP_WISE=y
CONFIG_THERMAL_HWMON=y
CONFIG_THERMAL_OF=y
CONFIG_UBIFS_FS=y
CONFIG_XXHASH=y
CONFIG_ZSTD_COMMON=y
@@ -256,7 +256,7 @@ static int msp430_pattern_set(struct led_classdev *led_cdev,
return 0;
}
static int msp430_led_probe(struct spi_device *spi, struct device_node *nc, u8 id)
static int msp430_led_probe(struct spi_device *spi, struct fwnode_handle *fw, u8 id)
{
struct device *dev = &spi->dev;
struct led_init_data init_data = {};
@@ -271,9 +271,9 @@ static int msp430_led_probe(struct spi_device *spi, struct device_node *nc, u8 i
led->id = id;
led->spi = spi;
init_data.fwnode = of_fwnode_handle(nc);
init_data.fwnode = fw;
state = led_init_default_state_get(init_data.fwnode);
state = led_init_default_state_get(fw);
switch (state) {
case LEDS_DEFSTATE_ON:
led->cdev.brightness = MSP430_LED_BRIGHTNESS_MAX;
@@ -326,17 +326,18 @@ static inline int msp430_check_workmode(struct spi_device *spi)
static int msp430_leds_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct device_node *np = dev_of_node(dev);
struct fwnode_handle *fw = dev_fwnode(dev);
struct fwnode_handle *child;
int rc;
rc = msp430_check_workmode(spi);
if (rc)
return rc;
for_each_available_child_of_node_scoped(np, child) {
fwnode_for_each_available_child_node(fw, child) {
u32 reg;
if (of_property_read_u32(child, "reg", &reg))
if (fwnode_property_read_u32(child, "reg", &reg))
continue;
if (reg < MSP430_LED_MIN_ID || reg > MSP430_LED_MAX_ID) {
@@ -346,8 +347,10 @@ static int msp430_leds_probe(struct spi_device *spi)
}
rc = msp430_led_probe(spi, child, reg);
if (rc < 0)
if (rc < 0) {
fwnode_handle_put(child);
return rc;
}
}
return 0;
@@ -0,0 +1,68 @@
From e0a77b1ed9001bdd0c73b99c67abb90f4bd7d7f5 Mon Sep 17 00:00:00 2001
From: George Moussalem <george.moussalem@outlook.com>
Date: Fri, 5 Jun 2026 12:11:29 +0400
Subject: [PATCH] net: dsa: qca8k: Add support for force mode for fixed link
topology
A fixed link topology is commonly used to connect this switch (on port
0 or 6) to a SoC's MAC over SGMII. When inband negotiation is not used,
the switch needs to be configured to operate in force mode. As such,
enable support for force mode.
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
Link: https://patch.msgid.link/20260605-qca8337-force-mode-v2-1-d9a6b6545bfa@outlook.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/dsa/qca/qca8k-8xxx.c | 22 ++++++++++++++++------
drivers/net/dsa/qca/qca8k.h | 1 +
2 files changed, 17 insertions(+), 6 deletions(-)
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1538,7 +1538,7 @@ static int qca8k_pcs_config(struct phyli
{
struct qca8k_priv *priv = pcs_to_qca8k_pcs(pcs)->priv;
int cpu_port_index, ret, port;
- u32 reg, val;
+ u32 mask, reg, val;
port = pcs_to_qca8k_pcs(pcs)->port;
switch (port) {
@@ -1611,11 +1611,21 @@ static int qca8k_pcs_config(struct phyli
if (priv->ports_config.sgmii_tx_clk_falling_edge)
val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE;
- if (val)
- ret = qca8k_rmw(priv, reg,
- QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
- QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
- val);
+ mask = (val) ? (QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
+ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE) : 0;
+
+ /*
+ * (Un)set force mode on QCA8337 only, don't include it in the mask for
+ * others. It is written to the PORT0 PAD register for both port 0 and 6.
+ */
+ if (priv->switch_id == QCA8K_ID_QCA8337) {
+ if (neg_mode == PHYLINK_PCS_NEG_OUTBAND)
+ val |= QCA8K_PORT_PAD_SGMII_FORCE_MODE;
+ mask |= QCA8K_PORT_PAD_SGMII_FORCE_MODE;
+ }
+
+ if (mask)
+ ret = qca8k_rmw(priv, reg, mask, val);
return 0;
}
--- a/drivers/net/dsa/qca/qca8k.h
+++ b/drivers/net/dsa/qca/qca8k.h
@@ -58,6 +58,7 @@
#define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25)
#define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24)
#define QCA8K_PORT_PAD_SGMII_EN BIT(7)
+#define QCA8K_PORT_PAD_SGMII_FORCE_MODE BIT(3)
#define QCA8K_REG_PWS 0x010
#define QCA8K_PWS_POWER_ON_SEL BIT(31)
/* This reg is only valid for QCA832x and toggle the package
@@ -1,21 +1,25 @@
From ec0031908d3abb24e791af2d5be9f3b0e265c945 Mon Sep 17 00:00:00 2001
From 0b7b378ce6cafbb948786cb6f17f406d94016c8c Mon Sep 17 00:00:00 2001
From: George Moussalem <george.moussalem@outlook.com>
Date: Fri, 25 Apr 2025 10:51:28 +0400
Subject: [PATCH] net: dsa: qca8k: fix led devicename when using external mdio bus
Date: Mon, 8 Jun 2026 11:22:08 +0400
Subject: [PATCH] net: dsa: qca8k: fix led devicename when using external mdio
bus
The qca8k dsa switch can use either an external or internal mdio bus.
This depends on whether the mdio node is defined under the switch node
itself and, as such, the internal_mdio_mask is populated with its
internal phys. Upon registering the internal mdio bus, the slave_mii_bus
itself. Upon registering the internal mdio bus, the internal_mdio_bus
of the dsa switch is assigned to this bus. When an external mdio bus is
used, it is left unassigned, though its id is used to create the device
names of the leds.
This leads to the leds being named '(efault):00:green:lan' and so on as
the slave_mii_bus is null. So let's fix this by adding a null check and
used, the driver still uses the internal_mdio_bus id which is used to
create the device names of the leds.
This leads to the leds being prefixed with '(efault)' as the
internal_mii_bus is null. So let's fix this by adding a null check and
use the devicename of the external bus instead when an external bus is
configured.
Fixes: 1e264f9d2918 ("net: dsa: qca8k: add LEDs basic support")
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20260608-qca8k-leds-fix-v3-1-a915bb2f37ae@outlook.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/dsa/qca/qca8k-leds.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
@@ -0,0 +1,68 @@
From e0a77b1ed9001bdd0c73b99c67abb90f4bd7d7f5 Mon Sep 17 00:00:00 2001
From: George Moussalem <george.moussalem@outlook.com>
Date: Fri, 5 Jun 2026 12:11:29 +0400
Subject: [PATCH] net: dsa: qca8k: Add support for force mode for fixed link
topology
A fixed link topology is commonly used to connect this switch (on port
0 or 6) to a SoC's MAC over SGMII. When inband negotiation is not used,
the switch needs to be configured to operate in force mode. As such,
enable support for force mode.
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
Link: https://patch.msgid.link/20260605-qca8337-force-mode-v2-1-d9a6b6545bfa@outlook.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/dsa/qca/qca8k-8xxx.c | 22 ++++++++++++++++------
drivers/net/dsa/qca/qca8k.h | 1 +
2 files changed, 17 insertions(+), 6 deletions(-)
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1538,7 +1538,7 @@ static int qca8k_pcs_config(struct phyli
{
struct qca8k_priv *priv = pcs_to_qca8k_pcs(pcs)->priv;
int cpu_port_index, ret, port;
- u32 reg, val;
+ u32 mask, reg, val;
port = pcs_to_qca8k_pcs(pcs)->port;
switch (port) {
@@ -1611,11 +1611,21 @@ static int qca8k_pcs_config(struct phyli
if (priv->ports_config.sgmii_tx_clk_falling_edge)
val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE;
- if (val)
- ret = qca8k_rmw(priv, reg,
- QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
- QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
- val);
+ mask = (val) ? (QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
+ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE) : 0;
+
+ /*
+ * (Un)set force mode on QCA8337 only, don't include it in the mask for
+ * others. It is written to the PORT0 PAD register for both port 0 and 6.
+ */
+ if (priv->switch_id == QCA8K_ID_QCA8337) {
+ if (neg_mode == PHYLINK_PCS_NEG_OUTBAND)
+ val |= QCA8K_PORT_PAD_SGMII_FORCE_MODE;
+ mask |= QCA8K_PORT_PAD_SGMII_FORCE_MODE;
+ }
+
+ if (mask)
+ ret = qca8k_rmw(priv, reg, mask, val);
return 0;
}
--- a/drivers/net/dsa/qca/qca8k.h
+++ b/drivers/net/dsa/qca/qca8k.h
@@ -58,6 +58,7 @@
#define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25)
#define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24)
#define QCA8K_PORT_PAD_SGMII_EN BIT(7)
+#define QCA8K_PORT_PAD_SGMII_FORCE_MODE BIT(3)
#define QCA8K_REG_PWS 0x010
#define QCA8K_PWS_POWER_ON_SEL BIT(31)
/* This reg is only valid for QCA832x and toggle the package
@@ -0,0 +1,38 @@
From 0b7b378ce6cafbb948786cb6f17f406d94016c8c Mon Sep 17 00:00:00 2001
From: George Moussalem <george.moussalem@outlook.com>
Date: Mon, 8 Jun 2026 11:22:08 +0400
Subject: [PATCH] net: dsa: qca8k: fix led devicename when using external mdio
bus
The qca8k dsa switch can use either an external or internal mdio bus.
This depends on whether the mdio node is defined under the switch node
itself. Upon registering the internal mdio bus, the internal_mdio_bus
of the dsa switch is assigned to this bus. When an external mdio bus is
used, the driver still uses the internal_mdio_bus id which is used to
create the device names of the leds.
This leads to the leds being prefixed with '(efault)' as the
internal_mii_bus is null. So let's fix this by adding a null check and
use the devicename of the external bus instead when an external bus is
configured.
Fixes: 1e264f9d2918 ("net: dsa: qca8k: add LEDs basic support")
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20260608-qca8k-leds-fix-v3-1-a915bb2f37ae@outlook.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/dsa/qca/qca8k-leds.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
--- a/drivers/net/dsa/qca/qca8k-leds.c
+++ b/drivers/net/dsa/qca/qca8k-leds.c
@@ -429,7 +429,8 @@ qca8k_parse_port_leds(struct qca8k_priv
init_data.fwnode = led;
init_data.devname_mandatory = true;
init_data.devicename = kasprintf(GFP_KERNEL, "%s:0%d",
- priv->internal_mdio_bus->id,
+ priv->internal_mdio_bus ?
+ priv->internal_mdio_bus->id : priv->bus->id,
port_num);
if (!init_data.devicename) {
fwnode_handle_put(led);
@@ -37,12 +37,9 @@ ar8327_phy_rgmii_set(struct ar8xxx_priv *priv, struct phy_device *phydev)
{
u16 phy_val = 0;
int phyaddr = phydev->mdio.addr;
struct device_node *np = phydev->mdio.dev.of_node;
struct device *dev = &phydev->mdio.dev;
if (!np)
return;
if (!of_property_read_bool(np, "qca,phy-rgmii-en")) {
if (!device_property_present(dev, "qca,phy-rgmii-en")) {
pr_err("ar8327: qca,phy-rgmii-en is not specified\n");
return;
}
@@ -53,7 +50,7 @@ ar8327_phy_rgmii_set(struct ar8xxx_priv *priv, struct phy_device *phydev)
AR8327_PHY_MODE_SEL, phy_val);
/* set rgmii tx clock delay if needed */
if (!of_property_read_bool(np, "qca,txclk-delay-en")) {
if (!device_property_present(dev, "qca,txclk-delay-en")) {
pr_err("ar8327: qca,txclk-delay-en is not specified\n");
return;
}
@@ -64,7 +61,7 @@ ar8327_phy_rgmii_set(struct ar8xxx_priv *priv, struct phy_device *phydev)
AR8327_PHY_SYS_CTRL, phy_val);
/* set rgmii rx clock delay if needed */
if (!of_property_read_bool(np, "qca,rxclk-delay-en")) {
if (!device_property_present(dev, "qca,rxclk-delay-en")) {
pr_err("ar8327: qca,rxclk-delay-en is not specified\n");
return;
}
@@ -364,31 +361,36 @@ ar8327_led_destroy(struct ar8327_led *aled)
}
static void
ar8327_leds_init(struct ar8xxx_priv *priv, struct device_node *leds)
ar8327_leds_init(struct ar8xxx_priv *priv, struct fwnode_handle *leds)
{
struct ar8327_data *data = priv->chip_data;
struct fwnode_handle *child;
unsigned i;
if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS))
return;
for_each_available_child_of_node_scoped(leds, child) {
fwnode_for_each_available_child_node(leds, child) {
u32 reg = 0, mode = 0;
struct ar8327_led_info info;
const char *name;
int ret;
ret = of_property_read_u32(child, "reg", &reg);
ret = fwnode_property_read_u32(child, "reg", &reg);
if (ret) {
pr_err("ar8327: LED %s is missing reg node\n", child->name);
pr_err("ar8327: LED %s is missing reg node\n", fwnode_get_name(child));
continue;
}
of_property_read_u32(child, "qca,led-mode", &mode);
fwnode_property_read_u32(child, "qca,led-mode", &mode);
if (fwnode_property_read_string(child, "label", &name))
name = fwnode_get_name(child);
info = (struct ar8327_led_info) {
.name = of_get_property(child, "label", NULL) ? : child->name,
.fwnode = of_fwnode_handle(child),
.active_low = of_property_read_bool(child, "active-low"),
.name = name,
.fwnode = child,
.active_low = fwnode_property_present(child, "active-low"),
.led_num = (enum ar8327_led_num) reg,
.mode = (enum ar8327_led_mode) mode
};
@@ -428,25 +430,29 @@ ar8327_leds_cleanup(struct ar8xxx_priv *priv)
}
static int
ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np)
ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device *dev)
{
struct ar8327_data *data = priv->chip_data;
const __be32 *paddr;
int len;
int i;
int len, i;
u32 *vals;
paddr = of_get_property(np, "qca,ar8327-initvals", &len);
if (!paddr || len < (2 * sizeof(*paddr)))
len = device_property_count_u32(dev, "qca,ar8327-initvals");
if (len < 2 || (len & 1))
return -EINVAL;
len /= sizeof(*paddr);
vals = kmalloc_array(len, sizeof(*vals), GFP_KERNEL);
if (!vals)
return -ENOMEM;
for (i = 0; i < len - 1; i += 2) {
u32 reg;
u32 val;
if (device_property_read_u32_array(dev, "qca,ar8327-initvals",
vals, len)) {
kfree(vals);
return -EINVAL;
}
reg = be32_to_cpup(paddr + i);
val = be32_to_cpup(paddr + i + 1);
for (i = 0; i < len; i += 2) {
u32 reg = vals[i];
u32 val = vals[i + 1];
switch (reg) {
case AR8327_REG_PORT_STATUS(0):
@@ -460,38 +466,43 @@ ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np)
break;
}
}
kfree(vals);
return 0;
}
static int
ar8327_hw_init(struct ar8xxx_priv *priv)
{
struct device_node *np = priv->pdev->of_node;
struct device_node *leds;
struct device *dev = priv->pdev;
struct fwnode_handle *child;
struct fwnode_handle *leds;
struct ar8327_data *data;
size_t count;
size_t count = 0;
int ret;
leds = of_get_child_by_name(np, "leds");
count = leds ? of_get_child_count(leds) : 0;
leds = device_get_named_child_node(dev, "leds");
if (leds)
fwnode_for_each_child_node(leds, child)
count++;
data = kzalloc(struct_size(data, leds, count), GFP_KERNEL);
if (!data) {
of_node_put(leds);
fwnode_handle_put(leds);
return -ENOMEM;
}
priv->chip_data = data;
ret = ar8327_hw_config_of(priv, np);
ret = ar8327_hw_config_of(priv, dev);
if (ret) {
of_node_put(leds);
fwnode_handle_put(leds);
return ret;
}
if (leds)
ar8327_leds_init(priv, leds);
of_node_put(leds);
fwnode_handle_put(leds);
ar8xxx_phy_init(priv);
@@ -16,7 +16,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -2031,6 +2031,8 @@ static const struct dsa_switch_ops qca8k
@@ -2041,6 +2041,8 @@ static const struct dsa_switch_ops qca8k
.port_fdb_add = qca8k_port_fdb_add,
.port_fdb_del = qca8k_port_fdb_del,
.port_fdb_dump = qca8k_port_fdb_dump,
@@ -72,7 +72,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
u32 val;
--- a/drivers/net/dsa/qca/qca8k.h
+++ b/drivers/net/dsa/qca/qca8k.h
@@ -592,5 +592,11 @@ int qca8k_port_lag_join(struct dsa_switc
@@ -593,5 +593,11 @@ int qca8k_port_lag_join(struct dsa_switc
struct netlink_ext_ack *extack);
int qca8k_port_lag_leave(struct dsa_switch *ds, int port,
struct dsa_lag lag);
@@ -14,7 +14,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1913,15 +1913,12 @@ qca8k_setup(struct dsa_switch *ds)
@@ -1923,15 +1923,12 @@ qca8k_setup(struct dsa_switch *ds)
}
}
@@ -26,7 +26,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1750,6 +1750,117 @@ qca8k_get_tag_protocol(struct dsa_switch
@@ -1760,6 +1760,117 @@ qca8k_get_tag_protocol(struct dsa_switch
return DSA_TAG_PROTO_QCA;
}
@@ -144,7 +144,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
static void
qca8k_conduit_change(struct dsa_switch *ds, const struct net_device *conduit,
bool operational)
@@ -2039,8 +2150,9 @@ static const struct dsa_switch_ops qca8k
@@ -2049,8 +2160,9 @@ static const struct dsa_switch_ops qca8k
.port_vlan_del = qca8k_port_vlan_del,
.phylink_get_caps = qca8k_phylink_get_caps,
.get_phy_flags = qca8k_get_phy_flags,
@@ -1,47 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Kevin Staley <glaciertablet@gmail.com>
Date: Mon, 29 Dec 2025 00:00:00 +0000
Subject: [PATCH] net: dsa: qca8k: merge host FDB for multi-CPU setups
In multi-CPU-port setups, DSA may install the same host FDB entry on
multiple upstream-facing ports when bridged user ports use different
conduits. qca8k_port_fdb_add currently overwrites any existing entry for
the same {MAC, VID}, so the last CPU port wins and host reachability can
break for ports serviced by the other CPU port (e.g. Netgear R7800 when
wan is bridged into br-lan).
Use the existing search-and-insert/delete helpers for CPU and DSA ports
so the destination port mask is merged instead of replaced.
Fixes: openwrt/openwrt#17891
Signed-off-by: Kevin Staley <glaciertablet@gmail.com>
---
drivers/net/dsa/qca/qca8k-common.c | 10 ++++++++++
1 file changed, 10 insertions(+)
--- a/drivers/net/dsa/qca/qca8k-common.c
+++ b/drivers/net/dsa/qca/qca8k-common.c
@@ -836,6 +836,13 @@ int qca8k_port_fdb_add(struct dsa_switch
struct qca8k_priv *priv = ds->priv;
u16 port_mask = BIT(port);
+ if (!vid)
+ vid = QCA8K_PORT_VID_DEF;
+
+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
+ return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid,
+ QCA8K_ATU_STATUS_STATIC);
+
return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
}
@@ -849,6 +856,9 @@ int qca8k_port_fdb_del(struct dsa_switch
if (!vid)
vid = QCA8K_PORT_VID_DEF;
+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
+ return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid);
+
return qca8k_fdb_del(priv, addr, port_mask, vid);
}
@@ -0,0 +1,71 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Olaf Marzocchi <lists@marzocchi.net>
Date: Fri, 26 Jun 2026 00:00:00 +0000
Subject: [PATCH] net: dsa: qca8k: don't program unicast host FDB entries on
CPU ports
DSA installs host FDB entries for the same {MAC, VID} once per CPU port
in use. qca8k used to overwrite the single ATU entry, so the last CPU
port won and host reachability broke for user ports served by the other
CPU port (e.g. Netgear R7800 with wan bridged into br-lan). Merging the
CPU port bits into the destination mask instead is no better: the ATU
forwards a unicast hit to every port set in the mask, so a host entry
spanning both CPU ports makes the switch deliver one copy of each
host-bound frame per CPU port. This duplicated nearly all host traffic
and halved throughput on TP-Link TGR1900, Archer C2600 and ASUS
RT-AX89X, with "failed to delete ... from fdb: -22" log spam from the
asymmetric delete path.
Host FDB entries are a pure optimization on this switch: the unknown
unicast forward mask in GLOBAL_FW_CTRL1 already contains all CPU ports
and is filtered per ingress port by PORT_LOOKUP_CTRL members, so a
host-bound frame with no ATU entry reaches exactly one CPU port, the
conduit of its ingress port, and the tag parser delivers it to the
correct user netdevice from either conduit. The conduits run in
promiscuous mode (qca8k does not implement FDB isolation), so no RX
filtering relies on these entries either.
Skip programming unicast host FDB entries on CPU ports entirely. This
fixes the multi-CPU bridged setups without ever creating a unicast
destination mask with more than one CPU port bit, regardless of how a
board assigns its conduits.
Disclaimer: patch generated via Fable 5 and tested by @xback
Fixes: openwrt/openwrt#21317
Signed-off-by: Olaf Marzocchi <lists@marzocchi.net>
---
drivers/net/dsa/qca/qca8k-common.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
--- a/drivers/net/dsa/qca/qca8k-common.c
+++ b/drivers/net/dsa/qca/qca8k-common.c
@@ -836,6 +836,17 @@ int qca8k_port_fdb_add(struct dsa_switch
struct qca8k_priv *priv = ds->priv;
u16 port_mask = BIT(port);
+ /* The ATU forwards a unicast hit to every port set in the
+ * destination mask, so a host entry spanning multiple CPU ports
+ * would deliver one copy of each host-bound frame per CPU port.
+ * Skip host entries entirely: unknown unicast is already forwarded
+ * to all CPU ports by GLOBAL_FW_CTRL1 and filtered by the ingress
+ * port LOOKUP_MEMBER, so host-bound frames reach exactly one CPU
+ * port, the conduit of the ingress port.
+ */
+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
+ return 0;
+
return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
}
@@ -846,6 +857,9 @@ int qca8k_port_fdb_del(struct dsa_switch
struct qca8k_priv *priv = ds->priv;
u16 port_mask = BIT(port);
+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
+ return 0;
+
if (!vid)
vid = QCA8K_PORT_VID_DEF;
@@ -0,0 +1,30 @@
From a4918445f34b80589a76cc047dd7d907b28b37ce Mon Sep 17 00:00:00 2001
From: Rosen Penev <rosenp@gmail.com>
Date: Sat, 27 Jun 2026 14:50:13 -0700
Subject: [PATCH] net: dsa: qca8k: fall back to ethernet-ports node name for
LEDs
The device tree binding allows both "ports" and "ethernet-ports" as
the container node name. Try "ethernet-ports" when "ports" is absent
so that newer DTBs with the preferred name work.
This matches the handling already present in qca8k-8xxx.c
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/dsa/qca/qca8k-leds.c | 3 +++
1 file changed, 3 insertions(+)
--- a/drivers/net/dsa/qca/qca8k-leds.c
+++ b/drivers/net/dsa/qca/qca8k-leds.c
@@ -457,6 +457,9 @@ qca8k_setup_led_ctrl(struct qca8k_priv *
int ret;
ports = device_get_named_child_node(priv->dev, "ports");
+ if (!ports)
+ ports = device_get_named_child_node(priv->dev, "ethernet-ports");
+
if (!ports) {
dev_info(priv->dev, "No ports node specified in device tree!");
return 0;
@@ -57,7 +57,7 @@ Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
/* Parse CPU port config to be later used in phy_link mac_config */
ret = qca8k_parse_port_config(priv);
if (ret)
@@ -2051,17 +2027,12 @@ qca8k_setup(struct dsa_switch *ds)
@@ -2045,17 +2021,12 @@ qca8k_setup(struct dsa_switch *ds)
if (ret)
return ret;
@@ -76,7 +76,7 @@ Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
QCA8K_PORT_LOOKUP_MEMBER,
@@ -2069,6 +2040,11 @@ qca8k_setup(struct dsa_switch *ds)
@@ -2068,6 +2039,11 @@ qca8k_setup(struct dsa_switch *ds)
if (ret)
return ret;
@@ -30,7 +30,7 @@ Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
static int qca8k_port_change_master(struct dsa_switch *ds, int port,
struct net_device *master,
struct netlink_ext_ack *extack)
@@ -2144,6 +2156,7 @@ static const struct dsa_switch_ops qca8k
@@ -2140,6 +2152,7 @@ static const struct dsa_switch_ops qca8k
.get_phy_flags = qca8k_get_phy_flags,
.port_lag_join = qca8xxx_port_lag_join,
.port_lag_leave = qca8xxx_port_lag_leave,
@@ -33,7 +33,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
}
/* Forward all unknown frames to CPU port for Linux processing */
@@ -2051,11 +2057,6 @@ qca8k_setup(struct dsa_switch *ds)
@@ -2046,11 +2052,6 @@ qca8k_setup(struct dsa_switch *ds)
if (ret)
return ret;
@@ -42,9 +42,9 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
- if (ret)
- return ret;
-
/* For port based vlans to work we need to set the
* default egress vid
*/
ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port),
BIT(port), BIT(port));
if (ret)
@@ -2107,6 +2108,9 @@ qca8k_setup(struct dsa_switch *ds)
/* Set max number of LAGs supported */
ds->num_lag_ids = QCA8K_NUM_LAGS;
@@ -16,7 +16,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -2030,6 +2030,8 @@ static const struct dsa_switch_ops qca8k
@@ -2040,6 +2040,8 @@ static const struct dsa_switch_ops qca8k
.port_fdb_add = qca8k_port_fdb_add,
.port_fdb_del = qca8k_port_fdb_del,
.port_fdb_dump = qca8k_port_fdb_dump,
@@ -72,7 +72,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
u32 val;
--- a/drivers/net/dsa/qca/qca8k.h
+++ b/drivers/net/dsa/qca/qca8k.h
@@ -591,5 +591,11 @@ int qca8k_port_lag_join(struct dsa_switc
@@ -592,5 +592,11 @@ int qca8k_port_lag_join(struct dsa_switc
struct netlink_ext_ack *extack);
int qca8k_port_lag_leave(struct dsa_switch *ds, int port,
struct dsa_lag lag);
@@ -14,7 +14,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1912,15 +1912,12 @@ qca8k_setup(struct dsa_switch *ds)
@@ -1922,15 +1922,12 @@ qca8k_setup(struct dsa_switch *ds)
}
}
@@ -26,7 +26,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1749,6 +1749,117 @@ qca8k_get_tag_protocol(struct dsa_switch
@@ -1759,6 +1759,117 @@ qca8k_get_tag_protocol(struct dsa_switch
return DSA_TAG_PROTO_QCA;
}
@@ -144,7 +144,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
static void
qca8k_conduit_change(struct dsa_switch *ds, const struct net_device *conduit,
bool operational)
@@ -2038,8 +2149,9 @@ static const struct dsa_switch_ops qca8k
@@ -2048,8 +2159,9 @@ static const struct dsa_switch_ops qca8k
.port_vlan_del = qca8k_port_vlan_del,
.phylink_get_caps = qca8k_phylink_get_caps,
.get_phy_flags = qca8k_get_phy_flags,
@@ -1,47 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Kevin Staley <glaciertablet@gmail.com>
Date: Mon, 29 Dec 2025 00:00:00 +0000
Subject: [PATCH] net: dsa: qca8k: merge host FDB for multi-CPU setups
In multi-CPU-port setups, DSA may install the same host FDB entry on
multiple upstream-facing ports when bridged user ports use different
conduits. qca8k_port_fdb_add currently overwrites any existing entry for
the same {MAC, VID}, so the last CPU port wins and host reachability can
break for ports serviced by the other CPU port (e.g. Netgear R7800 when
wan is bridged into br-lan).
Use the existing search-and-insert/delete helpers for CPU and DSA ports
so the destination port mask is merged instead of replaced.
Fixes: openwrt/openwrt#17891
Signed-off-by: Kevin Staley <glaciertablet@gmail.com>
---
drivers/net/dsa/qca/qca8k-common.c | 10 ++++++++++
1 file changed, 10 insertions(+)
--- a/drivers/net/dsa/qca/qca8k-common.c
+++ b/drivers/net/dsa/qca/qca8k-common.c
@@ -829,6 +829,13 @@ int qca8k_port_fdb_add(struct dsa_switch
struct qca8k_priv *priv = ds->priv;
u16 port_mask = BIT(port);
+ if (!vid)
+ vid = QCA8K_PORT_VID_DEF;
+
+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
+ return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid,
+ QCA8K_ATU_STATUS_STATIC);
+
return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
}
@@ -842,6 +849,9 @@ int qca8k_port_fdb_del(struct dsa_switch
if (!vid)
vid = QCA8K_PORT_VID_DEF;
+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
+ return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid);
+
return qca8k_fdb_del(priv, addr, port_mask, vid);
}
@@ -0,0 +1,71 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Olaf Marzocchi <lists@marzocchi.net>
Date: Fri, 26 Jun 2026 00:00:00 +0000
Subject: [PATCH] net: dsa: qca8k: don't program unicast host FDB entries on
CPU ports
DSA installs host FDB entries for the same {MAC, VID} once per CPU port
in use. qca8k used to overwrite the single ATU entry, so the last CPU
port won and host reachability broke for user ports served by the other
CPU port (e.g. Netgear R7800 with wan bridged into br-lan). Merging the
CPU port bits into the destination mask instead is no better: the ATU
forwards a unicast hit to every port set in the mask, so a host entry
spanning both CPU ports makes the switch deliver one copy of each
host-bound frame per CPU port. This duplicated nearly all host traffic
and halved throughput on TP-Link TGR1900, Archer C2600 and ASUS
RT-AX89X, with "failed to delete ... from fdb: -22" log spam from the
asymmetric delete path.
Host FDB entries are a pure optimization on this switch: the unknown
unicast forward mask in GLOBAL_FW_CTRL1 already contains all CPU ports
and is filtered per ingress port by PORT_LOOKUP_CTRL members, so a
host-bound frame with no ATU entry reaches exactly one CPU port, the
conduit of its ingress port, and the tag parser delivers it to the
correct user netdevice from either conduit. The conduits run in
promiscuous mode (qca8k does not implement FDB isolation), so no RX
filtering relies on these entries either.
Skip programming unicast host FDB entries on CPU ports entirely. This
fixes the multi-CPU bridged setups without ever creating a unicast
destination mask with more than one CPU port bit, regardless of how a
board assigns its conduits.
Disclaimer: patch generated via Fable 5 and tested by @xback
Fixes: openwrt/openwrt#21317
Signed-off-by: Olaf Marzocchi <lists@marzocchi.net>
---
drivers/net/dsa/qca/qca8k-common.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
--- a/drivers/net/dsa/qca/qca8k-common.c
+++ b/drivers/net/dsa/qca/qca8k-common.c
@@ -829,6 +829,17 @@ int qca8k_port_fdb_add(struct dsa_switch
struct qca8k_priv *priv = ds->priv;
u16 port_mask = BIT(port);
+ /* The ATU forwards a unicast hit to every port set in the
+ * destination mask, so a host entry spanning multiple CPU ports
+ * would deliver one copy of each host-bound frame per CPU port.
+ * Skip host entries entirely: unknown unicast is already forwarded
+ * to all CPU ports by GLOBAL_FW_CTRL1 and filtered by the ingress
+ * port LOOKUP_MEMBER, so host-bound frames reach exactly one CPU
+ * port, the conduit of the ingress port.
+ */
+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
+ return 0;
+
return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
}
@@ -839,6 +850,9 @@ int qca8k_port_fdb_del(struct dsa_switch
struct qca8k_priv *priv = ds->priv;
u16 port_mask = BIT(port);
+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
+ return 0;
+
if (!vid)
vid = QCA8K_PORT_VID_DEF;
@@ -0,0 +1,30 @@
From a4918445f34b80589a76cc047dd7d907b28b37ce Mon Sep 17 00:00:00 2001
From: Rosen Penev <rosenp@gmail.com>
Date: Sat, 27 Jun 2026 14:50:13 -0700
Subject: [PATCH] net: dsa: qca8k: fall back to ethernet-ports node name for
LEDs
The device tree binding allows both "ports" and "ethernet-ports" as
the container node name. Try "ethernet-ports" when "ports" is absent
so that newer DTBs with the preferred name work.
This matches the handling already present in qca8k-8xxx.c
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/dsa/qca/qca8k-leds.c | 3 +++
1 file changed, 3 insertions(+)
--- a/drivers/net/dsa/qca/qca8k-leds.c
+++ b/drivers/net/dsa/qca/qca8k-leds.c
@@ -457,6 +457,9 @@ qca8k_setup_led_ctrl(struct qca8k_priv *
int ret;
ports = device_get_named_child_node(priv->dev, "ports");
+ if (!ports)
+ ports = device_get_named_child_node(priv->dev, "ethernet-ports");
+
if (!ports) {
dev_info(priv->dev, "No ports node specified in device tree!");
return 0;
@@ -0,0 +1,58 @@
From e82cfa658fc4a0429b3d159d0e1bedb6b5cc6d38 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sat, 18 Jan 2025 16:18:40 +0800
Subject: [PATCH 1/3] net: dsa: qca8k: support PHY-to-PHY CPU link
PHY-to-PHY CPU link is a common/demo design in IPQ50xx platform, since it only has a SGMII/SGMII+ link and a MDI link.
For DSA, CPU tag is the only requirement. Fortunately, qca8337 can enable it on any port. So it's ok to trust a PHY-to-PHY link as a CPU link.
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
---
drivers/net/dsa/qca/qca8k-8xxx.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1014,7 +1014,7 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
return ret;
}
- if (!dsa_is_user_port(priv->ds, reg))
+ if (reg == 0 || reg == 6)
continue;
of_get_phy_mode(port, &mode);
@@ -1089,17 +1089,19 @@ qca8k_setup_mac_pwr_sel(struct qca8k_pri
static int qca8k_find_cpu_port(struct dsa_switch *ds)
{
- struct qca8k_priv *priv = ds->priv;
+ int i;
- /* Find the connected cpu port. Valid port are 0 or 6 */
if (dsa_is_cpu_port(ds, 0))
return 0;
- dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6");
-
if (dsa_is_cpu_port(ds, 6))
return 6;
+ /* PHY-to-PHY link */
+ for (i = 1; i <= 5; i++)
+ if (dsa_is_cpu_port(ds, i))
+ return i;
+
return -EINVAL;
}
@@ -1960,7 +1962,7 @@ qca8k_setup(struct dsa_switch *ds)
cpu_port = qca8k_find_cpu_port(ds);
if (cpu_port < 0) {
- dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6");
+ dev_err(priv->dev, "No cpu port configured");
return cpu_port;
}
@@ -0,0 +1,90 @@
From 08740093ef69e94613f19323f3abc55cb5d6863d Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Wed, 14 Jan 2026 23:38:30 +0800
Subject: [PATCH 2/3] net: dsa: qca8k: use correct CPU port when having multi
CPU ports
DSA framework call dsa_tree_setup_cpu_ports() to find a default CPU port.
If .preferred_default_local_cpu_port() isn't implemented, it will use the
first CPU port, which is different from qca8k_find_cpu_port().
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
---
drivers/net/dsa/qca/qca8k-8xxx.c | 38 ++++++--------------------------
1 file changed, 7 insertions(+), 31 deletions(-)
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1087,24 +1087,6 @@ qca8k_setup_mac_pwr_sel(struct qca8k_pri
return ret;
}
-static int qca8k_find_cpu_port(struct dsa_switch *ds)
-{
- int i;
-
- if (dsa_is_cpu_port(ds, 0))
- return 0;
-
- if (dsa_is_cpu_port(ds, 6))
- return 6;
-
- /* PHY-to-PHY link */
- for (i = 1; i <= 5; i++)
- if (dsa_is_cpu_port(ds, i))
- return i;
-
- return -EINVAL;
-}
-
static int
qca8k_setup_of_pws_reg(struct qca8k_priv *priv)
{
@@ -1957,15 +1939,9 @@ qca8k_setup(struct dsa_switch *ds)
{
struct qca8k_priv *priv = ds->priv;
struct dsa_port *dp;
- int cpu_port, ret;
+ int ret;
u32 mask;
- cpu_port = qca8k_find_cpu_port(ds);
- if (cpu_port < 0) {
- dev_err(priv->dev, "No cpu port configured");
- return cpu_port;
- }
-
/* Parse CPU port config to be later used in phy_link mac_config */
ret = qca8k_parse_port_config(priv);
if (ret)
@@ -2044,17 +2020,12 @@ qca8k_setup(struct dsa_switch *ds)
if (ret)
return ret;
- /* CPU port gets connected to all user ports of the switch */
- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port),
- QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds));
- if (ret)
- return ret;
-
/* Setup connection between CPU port & user ports
* Individual user ports get connected to CPU port only
*/
dsa_switch_for_each_user_port(dp, ds) {
u8 port = dp->index;
+ u8 cpu_port = dp->cpu_dp->index;
ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
QCA8K_PORT_LOOKUP_MEMBER,
@@ -2067,6 +2038,11 @@ qca8k_setup(struct dsa_switch *ds)
if (ret)
return ret;
+ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port),
+ BIT(port), BIT(port));
+ if (ret)
+ return ret;
+
/* For port based vlans to work we need to set the
* default egress vid
*/
@@ -0,0 +1,40 @@
From a572f1020159294fe980606996e7e3af5fa7ba9a Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Wed, 14 Jan 2026 23:38:30 +0800
Subject: [PATCH 3/3] net: dsa: qca8k: implement
ds->ops->preferred_default_local_cpu_port()
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
---
drivers/net/dsa/qca/qca8k-8xxx.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1743,6 +1743,18 @@ qca8k_get_tag_protocol(struct dsa_switch
return DSA_TAG_PROTO_QCA;
}
+static struct dsa_port *
+qca8k_preferred_default_local_cpu_port(struct dsa_switch *ds)
+{
+ if (dsa_is_cpu_port(ds, 0))
+ return dsa_to_port(ds, 0);
+
+ if (dsa_is_cpu_port(ds, 6))
+ return dsa_to_port(ds, 6);
+
+ return NULL;
+}
+
static int qca8k_port_change_master(struct dsa_switch *ds, int port,
struct net_device *master,
struct netlink_ext_ack *extack)
@@ -2139,6 +2151,7 @@ static const struct dsa_switch_ops qca8k
.get_phy_flags = qca8k_get_phy_flags,
.port_lag_join = qca8xxx_port_lag_join,
.port_lag_leave = qca8xxx_port_lag_leave,
+ .preferred_default_local_cpu_port = qca8k_preferred_default_local_cpu_port,
.port_change_conduit = qca8k_port_change_master,
.conduit_state_change = qca8k_conduit_change,
.connect_tag_protocol = qca8k_connect_tag_protocol,
@@ -33,7 +33,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
}
/* Forward all unknown frames to CPU port for Linux processing */
@@ -2050,11 +2056,6 @@ qca8k_setup(struct dsa_switch *ds)
@@ -2045,11 +2051,6 @@ qca8k_setup(struct dsa_switch *ds)
if (ret)
return ret;
@@ -42,9 +42,9 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
- if (ret)
- return ret;
-
/* For port based vlans to work we need to set the
* default egress vid
*/
ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(cpu_port),
BIT(port), BIT(port));
if (ret)
@@ -2106,6 +2107,9 @@ qca8k_setup(struct dsa_switch *ds)
/* Set max number of LAGs supported */
ds->num_lag_ids = QCA8K_NUM_LAGS;
@@ -1048,7 +1048,7 @@ Signed-off-by: Robert Marko <robert.marko@sartura.hr>
#define QCA8K_QCA832X_MIB_COUNT 39
#define QCA8K_QCA833X_MIB_COUNT 41
@@ -265,6 +269,7 @@
@@ -266,6 +270,7 @@
#define QCA8K_PORT_LOOKUP_STATE_LEARNING QCA8K_PORT_LOOKUP_STATE(0x3)
#define QCA8K_PORT_LOOKUP_STATE_FORWARD QCA8K_PORT_LOOKUP_STATE(0x4)
#define QCA8K_PORT_LOOKUP_LEARN BIT(20)
@@ -1056,7 +1056,7 @@ Signed-off-by: Robert Marko <robert.marko@sartura.hr>
#define QCA8K_PORT_LOOKUP_ING_MIRROR_EN BIT(25)
#define QCA8K_REG_GOL_TRUNK_CTRL0 0x700
@@ -341,6 +346,53 @@
@@ -342,6 +347,53 @@
#define MII_ATH_MMD_ADDR 0x0d
#define MII_ATH_MMD_DATA 0x0e
@@ -1110,7 +1110,7 @@ Signed-off-by: Robert Marko <robert.marko@sartura.hr>
enum {
QCA8K_PORT_SPEED_10M = 0,
QCA8K_PORT_SPEED_100M = 1,
@@ -468,6 +520,10 @@ struct qca8k_priv {
@@ -469,6 +521,10 @@ struct qca8k_priv {
struct qca8k_pcs pcs_port_6;
const struct qca8k_match_data *info;
struct qca8k_led ports_led[QCA8K_LED_COUNT];
@@ -1061,7 +1061,7 @@ Signed-off-by: Robert Marko <robert.marko@sartura.hr>
#define QCA8K_QCA832X_MIB_COUNT 39
#define QCA8K_QCA833X_MIB_COUNT 41
@@ -265,6 +269,7 @@
@@ -266,6 +270,7 @@
#define QCA8K_PORT_LOOKUP_STATE_LEARNING QCA8K_PORT_LOOKUP_STATE(0x3)
#define QCA8K_PORT_LOOKUP_STATE_FORWARD QCA8K_PORT_LOOKUP_STATE(0x4)
#define QCA8K_PORT_LOOKUP_LEARN BIT(20)
@@ -1069,7 +1069,7 @@ Signed-off-by: Robert Marko <robert.marko@sartura.hr>
#define QCA8K_PORT_LOOKUP_ING_MIRROR_EN BIT(25)
#define QCA8K_REG_GOL_TRUNK_CTRL0 0x700
@@ -341,6 +346,53 @@
@@ -342,6 +347,53 @@
#define MII_ATH_MMD_ADDR 0x0d
#define MII_ATH_MMD_DATA 0x0e
@@ -1123,7 +1123,7 @@ Signed-off-by: Robert Marko <robert.marko@sartura.hr>
enum {
QCA8K_PORT_SPEED_10M = 0,
QCA8K_PORT_SPEED_100M = 1,
@@ -468,6 +520,10 @@ struct qca8k_priv {
@@ -469,6 +521,10 @@ struct qca8k_priv {
struct qca8k_pcs pcs_port_6;
const struct qca8k_match_data *info;
struct qca8k_led ports_led[QCA8K_LED_COUNT];
@@ -322,7 +322,7 @@
read-only;
};
factory: partition@2b0000 {
partition@2b0000 {
label = "PRODUCTDATA";
reg = <0x02b0000 0x0030000>;
read-only;
@@ -308,7 +308,7 @@
reg = <0x040000 0x010000>;
};
factory: partition@50000 {
partition@50000 {
label = "factory";
reg = <0x050000 0x0b0000>;
read-only;
@@ -147,7 +147,7 @@
read-only;
};
factory: partition@180000 {
partition@180000 {
label = "Factory";
reg = <0x180000 0x0200000>;
read-only;
@@ -27,6 +27,16 @@
reg = <0 0x40000000 0 0x40000000>;
};
usb_vbus: regulator-usb-vbus {
compatible = "regulator-fixed";
regulator-name = "usb_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
gpios = <&pio 14 GPIO_ACTIVE_HIGH>;
enable-active-high;
regulator-boot-on;
};
gpio-keys {
compatible = "gpio-keys";
@@ -221,5 +231,6 @@
};
&xhci {
vbus-supply = <&usb_vbus>;
status = "okay";
};
@@ -186,7 +186,7 @@
reg = <0x100000 0x80000>;
};
factory: partition@180000 {
partition@180000 {
label = "Factory";
reg = <0x180000 0x200000>;
read-only;
@@ -120,10 +120,26 @@
read-only;
};
factory: partition@180000 {
partition@180000 {
label = "Factory";
reg = <0x180000 0x200000>;
read-only;
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
eeprom_factory_0: eeprom@0 {
reg = <0x0 0x1000>;
};
macaddr_factory_1fef20: macaddr@1fef20 {
compatible = "mac-base";
reg = <0x1fef20 0x6>;
#nvmem-cell-cells = <1>;
};
};
};
partition@380000 {
@@ -155,24 +171,6 @@
};
};
&factory {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
eeprom_factory_0: eeprom@0 {
reg = <0x0 0x1000>;
};
macaddr_factory_1fef20: macaddr@1fef20 {
compatible = "mac-base";
reg = <0x1fef20 0x6>;
#nvmem-cell-cells = <1>;
};
};
};
&pio {
spi0_flash_pins: spi0-pins {
mux {
@@ -204,7 +204,7 @@
read-only;
};
factory: partition@180000 {
partition@180000 {
label = "Factory";
reg = <0x180000 0x200000>;
read-only;
@@ -370,9 +370,10 @@
reg = <0x100000 0x80000>;
};
factory: partition@180000 {
partition@180000 {
label = "Factory";
reg = <0x180000 0x400000>;
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
@@ -109,17 +109,18 @@ srg_led_set_brightness(struct led_classdev *led_cdev,
}
static int
srg_led_init_led(struct srg_led_ctrl *sysled_ctrl, struct device_node *np)
srg_led_init_led(struct srg_led_ctrl *sysled_ctrl, struct fwnode_handle *fw)
{
struct led_init_data init_data = {};
struct led_classdev *led_cdev;
struct srg_led *sysled;
const char *name;
int index, ret;
if (!np)
if (!fw)
return -ENOENT;
ret = of_property_read_u32(np, "reg", &index);
ret = fwnode_property_read_u32(fw, "reg", &index);
if (ret) {
dev_err(&sysled_ctrl->client->dev,
"srg_led_init_led: no reg defined in np!\n");
@@ -135,9 +136,12 @@ srg_led_init_led(struct srg_led_ctrl *sysled_ctrl, struct device_node *np)
sysled->index = index;
sysled->ctrl = sysled_ctrl;
init_data.fwnode = of_fwnode_handle(np);
init_data.fwnode = fw;
led_cdev->name = of_get_property(np, "label", NULL) ? : np->name;
if (fwnode_property_read_string(fw, "label", &name))
name = fwnode_get_name(fw);
led_cdev->name = name;
led_cdev->brightness = LED_OFF;
led_cdev->max_brightness = LED_FULL;
led_cdev->brightness_set_blocking = srg_led_set_brightness;
@@ -161,23 +165,24 @@ static int
srg_led_probe(struct i2c_client *client)
{
struct device_node *np = client->dev.of_node;
struct device *dev = &client->dev;
struct fwnode_handle *fw = dev_fwnode(dev);
struct srg_led_ctrl *sysled_ctrl;
int err;
sysled_ctrl = devm_kzalloc(&client->dev, sizeof(*sysled_ctrl), GFP_KERNEL);
sysled_ctrl = devm_kzalloc(dev, sizeof(*sysled_ctrl), GFP_KERNEL);
if (!sysled_ctrl)
return -ENOMEM;
sysled_ctrl->client = client;
err = devm_mutex_init(&client->dev, &sysled_ctrl->lock);
err = devm_mutex_init(dev, &sysled_ctrl->lock);
if (err)
return err;
i2c_set_clientdata(client, sysled_ctrl);
for_each_available_child_of_node_scoped(np, child) {
fwnode_for_each_available_child_node_scoped(fw, child) {
if (srg_led_init_led(sysled_ctrl, child))
continue;
+1 -1
View File
@@ -1902,7 +1902,7 @@ define Device/globitel_bt-r320
KERNEL := kernel-bin | lzma
KERNEL_INITRAMFS := kernel-bin | lzma | \
fit lzma $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb with-initrd | pad-to 64k
KERNEL_INITRAMFS_SUFFIX := -initramfs-recovery.itb
KERNEL_INITRAMFS_SUFFIX := -recovery.itb
IMAGES := sysupgrade.itb
IMAGE_SIZE := $$(shell expr 64 + $$(CONFIG_TARGET_ROOTFS_PARTSIZE))m
IMAGE/sysupgrade.itb := append-kernel | \
@@ -1,9 +1,9 @@
From 074d87ae70e2b549001c31f87f2a585225f2e99d Mon Sep 17 00:00:00 2001
From 2ad75da9a7b7fa1fed92624685c0e9c25fcad719 Mon Sep 17 00:00:00 2001
From: Robert Marko <robert.marko@sartura.hr>
Date: Wed, 1 Jul 2026 15:37:32 +0200
Subject: [PATCH] net: sparx5: configure TAS port link speed
Subject: [RFC net-next] net: sparx5: configure TAS port link speed
On the TSN and RED variants of SparX-5 and LAN969x TAS (Time-Aware Shaper)
On the TSN and RED variants of LAN969x and SparX-5i TAS (Time-Aware Shaper)
is present in the silicon.
Currently, the driver does not use configure it at all, which means that
@@ -29,8 +29,9 @@ Signed-off-by: Robert Marko <robert.marko@sartura.hr>
.../ethernet/microchip/sparx5/sparx5_port.c | 4 ++
.../ethernet/microchip/sparx5/sparx5_qos.c | 49 +++++++++++++++++++
.../ethernet/microchip/sparx5/sparx5_qos.h | 1 +
.../ethernet/microchip/sparx5/sparx5_regs.c | 3 ++
.../ethernet/microchip/sparx5/sparx5_regs.h | 3 ++
6 files changed, 72 insertions(+)
7 files changed, 75 insertions(+)
--- a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c
+++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c
@@ -171,6 +172,32 @@ Signed-off-by: Robert Marko <robert.marko@sartura.hr>
/* Multi-Queue Priority */
int sparx5_tc_mqprio_add(struct net_device *ndev, u8 num_tc);
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c
@@ -95,6 +95,7 @@ const unsigned int sparx5_gaddr[GADDR_LA
[GA_HSCH_SYSTEM] = 184000,
[GA_HSCH_MMGT] = 162368,
[GA_HSCH_TAS_CONFIG] = 162384,
+ [GA_HSCH_TAS_PROFILE_CFG] = 188416,
[GA_PTP_PTP_CFG] = 320,
[GA_PTP_PTP_TOD_DOMAINS] = 336,
[GA_PTP_PHASE_DETECTOR_CTRL] = 420,
@@ -129,6 +130,7 @@ const unsigned int sparx5_gcnt[GCNT_LAST
[GC_GCB_SIO_CTRL] = 3,
[GC_HSCH_HSCH_CFG] = 5040,
[GC_HSCH_HSCH_DWRR] = 72,
+ [GC_HSCH_TAS_PROFILE_CFG] = 100,
[GC_PTP_PTP_PINS] = 5,
[GC_PTP_PHASE_DETECTOR_CTRL] = 5,
[GC_REW_PORT] = 70,
@@ -144,6 +146,7 @@ const unsigned int sparx5_gsize[GSIZE_LA
[GW_FDMA_FDMA] = 428,
[GW_GCB_CHIP_REGS] = 424,
[GW_HSCH_TAS_CONFIG] = 12,
+ [GW_HSCH_TAS_PROFILE_CFG] = 64,
[GW_PTP_PHASE_DETECTOR_CTRL] = 8,
[GW_QSYS_PAUSE_CFG] = 1128,
};
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h
@@ -104,6 +104,7 @@ enum sparx5_gaddr_enum {
@@ -1,158 +0,0 @@
From patchwork Fri Jun 5 08:11:29 2026
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: George Moussalem <george.moussalem@outlook.com>
X-Patchwork-Id: 14612861
X-Patchwork-Delegate: kuba@kernel.org
Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org
[10.30.226.201])
(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
(No client certificate requested)
by smtp.subspace.kernel.org (Postfix) with ESMTPS id A30B6495510;
Fri, 5 Jun 2026 08:11:31 +0000 (UTC)
Authentication-Results: smtp.subspace.kernel.org;
arc=none smtp.client-ip=10.30.226.201
ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;
t=1780647091; cv=none;
b=izElTZnAl4VU4jMwjDaYCFw9xgx5Jdm3nvzLj6yKtIxqBkHhndvHgZTl1ps4ZXGZY7WuzqrPlnZzKvjqbmLl9zMojNhz9bfdRUSxAHckbIK7FvDAqJkdGnW7vUVhM2Y2vqBfHF8y99Y7jlplG33EUJvsGus3U3Nwb3XrMMA+TEk=
ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org;
s=arc-20240116; t=1780647091; c=relaxed/simple;
bh=wVsP7qwG8BJu2wprX0HV2M1fTkH7tQV3G1mR8NtSBaw=;
h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc;
b=YkSoB8rVHO59IG66zmiHuYuyYWLzbyGnZ4+Pyj30JYpjAyBu7MB3zgNmma5FE0yJ7yiQycoKrPcRux8qX7HpzavyQwPsDNmhSJ3eRqrE70gLAOVn8Aia4AnJW0rE5tcNyS6PhhYfflZLwAMqrMF9wuhVOgl8HQ/nT/H2Xry/x8Q=
ARC-Authentication-Results: i=1; smtp.subspace.kernel.org;
dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org
header.b=Hbrnqrg9; arc=none smtp.client-ip=10.30.226.201
Authentication-Results: smtp.subspace.kernel.org;
dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org
header.b="Hbrnqrg9"
Received: by smtp.kernel.org (Postfix) with ESMTPS id 4CA61C2BCC6;
Fri, 5 Jun 2026 08:11:31 +0000 (UTC)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org;
s=k20201202; t=1780647091;
bh=wVsP7qwG8BJu2wprX0HV2M1fTkH7tQV3G1mR8NtSBaw=;
h=From:Date:Subject:To:Cc:Reply-To:From;
b=Hbrnqrg9RhUWrD93DON6WYHKiUY/P9JoQoOKcBb0/KJWXzbzKUzy1aiEPRDNCz74l
unOk46jE02c/euDZYR1gDWxh9Uxacss0xlUunTTj86g+Uk7iSNaM04wzH4DGgAqWuP
QOK6c9mcDNCgEQ6otxAJVE8JDdQ6Rci/uyPf3YSW6lk2Xc5jca5/hSBn1Ed49K5Ycp
e3VdODdZns9EJlfW2FyX1pLCLijdAZOhdw0+BdfL7Jl8v1NW3lPx68kX0BRG1rAFRs
fIOgpgls+EJeJW0zsiaY65LScSz0F1I6vkcMUyGlEv/qY6jPK7TwZ9GbG+DUyC7SF3
jl5FVBD61KZUQ==
Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org
(localhost.localdomain [127.0.0.1])
by smtp.lore.kernel.org (Postfix) with ESMTP id 305B6CD6E5D;
Fri, 5 Jun 2026 08:11:31 +0000 (UTC)
Date: Fri, 05 Jun 2026 12:11:29 +0400
Subject: [PATCH next-next v2] net: dsa: qca8k: Add support for force mode
for fixed link topology
Precedence: bulk
X-Mailing-List: netdev@vger.kernel.org
List-Id: <netdev.vger.kernel.org>
List-Subscribe: <mailto:netdev+subscribe@vger.kernel.org>
List-Unsubscribe: <mailto:netdev+unsubscribe@vger.kernel.org>
MIME-Version: 1.0
Message-Id: <20260605-qca8337-force-mode-v2-1-d9a6b6545bfa@outlook.com>
X-B4-Tracking: v=1; b=H4sIAAAAAAAC/32NTQ6CMBCFr0Jm7ZjSYlFX3sOwKGUqjdLRFgmGc
HcrB3Dzki/vb4FE0VOCc7FApMknzyGD3BVgexNuhL7LDFJILbRQ+LLmqFSNjqMlHLgjdK1tzUG
edFVpyMVnJOfnbfQKgeYRfwJNtnqfRo6f7W4qt8C/5anEEq1xta6Fc6ZVF36PD+b73vIAzbquX
0/JJvLBAAAA
X-Change-ID: 20260603-qca8337-force-mode-fbcba5296446
To: Andrew Lunn <andrew@lunn.ch>, Vladimir Oltean <olteanv@gmail.com>,
"David S. Miller" <davem@davemloft.net>, Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
George Moussalem <george.moussalem@outlook.com>
X-Mailer: b4 0.14.3
X-Developer-Signature: v=1; a=ed25519-sha256; t=1780647089; l=3187;
i=george.moussalem@outlook.com; s=20250321; h=from:subject:message-id;
bh=/1K49g/Q0+wrB8ffa0QJHaW9eANv5Yae+dqQkwbdI4U=;
b=Xn7RwqPiLUqBSMkfjxaoyatDwqn+EhlJZVxXog3mgyAtwLsVAImKiBsVWVvKVPlXJ//K6hwlv
IPyVfdFw1CBAI5t89drX8j9nLQevPmF9+g75OyKnuqOsk4jD9ZFIckZ
X-Developer-Key: i=george.moussalem@outlook.com; a=ed25519;
pk=/PuRTSI9iYiHwcc6Nrde8qF4ZDhJBlUgpHdhsIjnqIk=
X-Endpoint-Received: by B4 Relay for george.moussalem@outlook.com/20250321
with auth_id=364
X-Original-From: George Moussalem <george.moussalem@outlook.com>
Reply-To: george.moussalem@outlook.com
From: George Moussalem <george.moussalem@outlook.com>
X-Patchwork-Delegate: kuba@kernel.org
From: George Moussalem <george.moussalem@outlook.com>
A fixed link topology is commonly used to connect this switch (on port
0 or 6) to a SoC's MAC over SGMII. When inband negotiation is not used,
the switch needs to be configured to operate in force mode. As such,
enable support for force mode.
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
Changes in v2:
- Added check for QCA8337 switch ID and build up the register mask
accordingly before writing the force mode bit to avoid overwriting the
bit on switches other than QCA8337 for which there's no documentation
on whether force mode is supported or not.
- Added comment to state that regardless of port used (0 or 6), the
force mode bit is written to the PORT0 PAD register as per vendor SDK.
- Link to v1: https://lore.kernel.org/r/20260603-qca8337-force-mode-v1-1-caf7670ffab3@outlook.com
---
drivers/net/dsa/qca/qca8k-8xxx.c | 22 ++++++++++++++++------
drivers/net/dsa/qca/qca8k.h | 1 +
2 files changed, 17 insertions(+), 6 deletions(-)
---
base-commit: b7bee4ca5688e30ca50fbc87b1b8f7eed7006c17
change-id: 20260603-qca8337-force-mode-fbcba5296446
Best regards,
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1538,7 +1538,7 @@ static int qca8k_pcs_config(struct phyli
{
struct qca8k_priv *priv = pcs_to_qca8k_pcs(pcs)->priv;
int cpu_port_index, ret, port;
- u32 reg, val;
+ u32 mask, reg, val;
port = pcs_to_qca8k_pcs(pcs)->port;
switch (port) {
@@ -1611,11 +1611,21 @@ static int qca8k_pcs_config(struct phyli
if (priv->ports_config.sgmii_tx_clk_falling_edge)
val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE;
- if (val)
- ret = qca8k_rmw(priv, reg,
- QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
- QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE,
- val);
+ mask = (val) ? (QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE |
+ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE) : 0;
+
+ /*
+ * (Un)set force mode on QCA8337 only, don't include it in the mask for
+ * others. It is written to the PORT0 PAD register for both port 0 and 6.
+ */
+ if (priv->switch_id == QCA8K_ID_QCA8337) {
+ if (neg_mode == PHYLINK_PCS_NEG_OUTBAND)
+ val |= QCA8K_PORT_PAD_SGMII_FORCE_MODE;
+ mask |= QCA8K_PORT_PAD_SGMII_FORCE_MODE;
+ }
+
+ if (mask)
+ ret = qca8k_rmw(priv, reg, mask, val);
return 0;
}
--- a/drivers/net/dsa/qca/qca8k.h
+++ b/drivers/net/dsa/qca/qca8k.h
@@ -58,6 +58,7 @@
#define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25)
#define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24)
#define QCA8K_PORT_PAD_SGMII_EN BIT(7)
+#define QCA8K_PORT_PAD_SGMII_FORCE_MODE BIT(3)
#define QCA8K_REG_PWS 0x010
#define QCA8K_PWS_POWER_ON_SEL BIT(31)
/* This reg is only valid for QCA832x and toggle the package
@@ -94,7 +94,7 @@
read-only;
};
factory: partition@40000 {
partition@40000 {
label = "factory";
reg = <0x40000 0x10000>;
read-only;
@@ -94,7 +94,7 @@
read-only;
};
factory: partition@40000 {
partition@40000 {
label = "factory";
reg = <0x40000 0x10000>;
read-only;
@@ -164,8 +164,6 @@
reg = <0x0000 0 0 0 0>;
nvmem-cells = <&eeprom_factory_0>;
nvmem-cell-names = "eeprom";
// Wi-Fi device reads it's MAC address from EEPROM (&factory + 4)
// adding anything related to mac-address here will cause use random MAC
};
};
@@ -175,8 +173,6 @@
reg = <0x0000 0 0 0 0>;
nvmem-cells = <&eeprom_factory_8000>;
nvmem-cell-names = "eeprom";
// Wi-Fi device reads it's MAC address from EEPROM, (&factory + 0x8000 + 4)
// adding anything related to mac-address here will cause use random MAC.
};
};
@@ -110,7 +110,7 @@
read-only;
};
factory: partition@100000 {
partition@100000 {
label = "factory";
reg = <0x100000 0x40000>;
read-only;
@@ -49,18 +49,12 @@
nvmem-cell-names = "mac-address";
};
&factory {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
&nvmem {
macaddr_factory_fff4: macaddr@fff4 {
reg = <0xfff4 0x6>;
};
macaddr_factory_fff4: macaddr@fff4 {
reg = <0xfff4 0x6>;
};
macaddr_factory_fffa: macaddr@fffa {
reg = <0xfffa 0x6>;
};
macaddr_factory_fffa: macaddr@fffa {
reg = <0xfffa 0x6>;
};
};
@@ -54,24 +54,18 @@
nvmem-cell-names = "eeprom", "mac-address";
};
&factory {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
&nvmem {
macaddr_factory_4: macaddr@4 {
compatible = "mac-base";
reg = <0x4 0x6>;
#nvmem-cell-cells = <1>;
};
macaddr_factory_4: macaddr@4 {
compatible = "mac-base";
reg = <0x4 0x6>;
#nvmem-cell-cells = <1>;
};
macaddr_factory_fff4: macaddr@fff4 {
reg = <0xfff4 0x6>;
};
macaddr_factory_fff4: macaddr@fff4 {
reg = <0xfff4 0x6>;
};
macaddr_factory_fffa: macaddr@fffa {
reg = <0xfffa 0x6>;
};
macaddr_factory_fffa: macaddr@fffa {
reg = <0xfffa 0x6>;
};
};
@@ -63,20 +63,14 @@
nvmem-cell-names = "eeprom", "mac-address";
};
&factory {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
&nvmem {
macaddr_factory_4: macaddr@4 {
compatible = "mac-base";
reg = <0x4 0x6>;
#nvmem-cell-cells = <1>;
};
macaddr_factory_4: macaddr@4 {
compatible = "mac-base";
reg = <0x4 0x6>;
#nvmem-cell-cells = <1>;
};
macaddr_factory_fff4: macaddr@fff4 {
reg = <0xfff4 0x6>;
};
macaddr_factory_fff4: macaddr@fff4 {
reg = <0xfff4 0x6>;
};
};
@@ -54,24 +54,18 @@
nvmem-cell-names = "eeprom", "mac-address";
};
&factory {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
&nvmem {
macaddr_factory_4: macaddr@4 {
compatible = "mac-base";
reg = <0x4 0x6>;
#nvmem-cell-cells = <1>;
};
macaddr_factory_4: macaddr@4 {
compatible = "mac-base";
reg = <0x4 0x6>;
#nvmem-cell-cells = <1>;
};
macaddr_factory_fff4: macaddr@fff4 {
reg = <0xfff4 0x6>;
};
macaddr_factory_fff4: macaddr@fff4 {
reg = <0xfff4 0x6>;
};
macaddr_factory_fffa: macaddr@fffa {
reg = <0xfffa 0x6>;
};
macaddr_factory_fffa: macaddr@fffa {
reg = <0xfffa 0x6>;
};
};
@@ -54,20 +54,14 @@
nvmem-cell-names = "eeprom", "mac-address";
};
&factory {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
&nvmem {
macaddr_factory_e000: macaddr@e000 {
reg = <0xe000 0x6>;
};
macaddr_factory_e000: macaddr@e000 {
reg = <0xe000 0x6>;
};
macaddr_factory_e006: macaddr@e006 {
compatible = "mac-base";
reg = <0xe006 0x6>;
#nvmem-cell-cells = <1>;
};
macaddr_factory_e006: macaddr@e006 {
compatible = "mac-base";
reg = <0xe006 0x6>;
#nvmem-cell-cells = <1>;
};
};
@@ -49,18 +49,12 @@
nvmem-cell-names = "mac-address";
};
&factory {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
&nvmem {
macaddr_factory_e000: macaddr@e000 {
reg = <0xe000 0x6>;
};
macaddr_factory_e000: macaddr@e000 {
reg = <0xe000 0x6>;
};
macaddr_factory_e006: macaddr@e006 {
reg = <0xe006 0x6>;
};
macaddr_factory_e006: macaddr@e006 {
reg = <0xe006 0x6>;
};
};
@@ -49,18 +49,12 @@
nvmem-cell-names = "mac-address";
};
&factory {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
&nvmem {
macaddr_factory_e000: macaddr@e000 {
reg = <0xe000 0x6>;
};
macaddr_factory_e000: macaddr@e000 {
reg = <0xe000 0x6>;
};
macaddr_factory_e006: macaddr@e006 {
reg = <0xe006 0x6>;
};
macaddr_factory_e006: macaddr@e006 {
reg = <0xe006 0x6>;
};
};
@@ -49,18 +49,12 @@
nvmem-cell-names = "mac-address";
};
&factory {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
&nvmem {
macaddr_factory_e000: macaddr@e000 {
reg = <0xe000 0x6>;
};
macaddr_factory_e000: macaddr@e000 {
reg = <0xe000 0x6>;
};
macaddr_factory_e006: macaddr@e006 {
reg = <0xe006 0x6>;
};
macaddr_factory_e006: macaddr@e006 {
reg = <0xe006 0x6>;
};
};
@@ -49,18 +49,12 @@
nvmem-cell-names = "mac-address";
};
&factory {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
&nvmem {
macaddr_factory_e000: macaddr@e000 {
reg = <0xe000 0x6>;
};
macaddr_factory_e000: macaddr@e000 {
reg = <0xe000 0x6>;
};
macaddr_factory_e006: macaddr@e006 {
reg = <0xe006 0x6>;
};
macaddr_factory_e006: macaddr@e006 {
reg = <0xe006 0x6>;
};
};
@@ -49,18 +49,12 @@
nvmem-cell-names = "mac-address";
};
&factory {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
&nvmem {
macaddr_factory_fff4: macaddr@fff4 {
reg = <0xfff4 0x6>;
};
macaddr_factory_fff4: macaddr@fff4 {
reg = <0xfff4 0x6>;
};
macaddr_factory_fffa: macaddr@fffa {
reg = <0xfffa 0x6>;
};
macaddr_factory_fffa: macaddr@fffa {
reg = <0xfffa 0x6>;
};
};
@@ -47,18 +47,12 @@
nvmem-cell-names = "mac-address";
};
&factory {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
&nvmem {
macaddr_factory_e000: macaddr@e000 {
reg = <0xe000 0x6>;
};
macaddr_factory_e000: macaddr@e000 {
reg = <0xe000 0x6>;
};
macaddr_factory_e006: macaddr@e006 {
reg = <0xe006 0x6>;
};
macaddr_factory_e006: macaddr@e006 {
reg = <0xe006 0x6>;
};
};
@@ -49,18 +49,12 @@
nvmem-cell-names = "mac-address";
};
&factory {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
&nvmem {
macaddr_factory_e000: macaddr@e000 {
reg = <0xe000 0x6>;
};
macaddr_factory_e000: macaddr@e000 {
reg = <0xe000 0x6>;
};
macaddr_factory_e006: macaddr@e006 {
reg = <0xe006 0x6>;
};
macaddr_factory_e006: macaddr@e006 {
reg = <0xe006 0x6>;
};
};
@@ -147,12 +147,12 @@
read-only;
};
factory: partition@40000 {
partition@40000 {
label = "factory";
reg = <0x40000 0x10000>;
read-only;
nvmem-layout {
nvmem: nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
@@ -92,7 +92,7 @@
reg = <0x80000 0x80000>;
};
factory: partition@100000 {
partition@100000 {
label = "Factory";
reg = <0x100000 0x40000>;
read-only;
@@ -83,7 +83,7 @@
read-only;
};
factory: partition@40000 {
partition@40000 {
label = "factory";
reg = <0x40000 0x10000>;
read-only;
@@ -46,12 +46,12 @@
read-only;
};
factory: partition@fd0000 {
partition@fd0000 {
label = "factory";
reg = <0xfd0000 0x30000>;
read-only;
nvmem-layout {
nvmem: nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
@@ -44,12 +44,12 @@
read-only;
};
factory: partition@7d0000 {
partition@7d0000 {
label = "factory";
reg = <0x7d0000 0x30000>;
read-only;
nvmem-layout {
nvmem: nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
@@ -100,16 +100,10 @@
nvmem-cell-names = "mac-address";
};
&factory {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
macaddr_factory_1f100: macaddr@1f100 {
compatible = "mac-base";
reg = <0x1f100 0x6>;
#nvmem-cell-cells = <1>;
};
&nvmem {
macaddr_factory_1f100: macaddr@1f100 {
compatible = "mac-base";
reg = <0x1f100 0x6>;
#nvmem-cell-cells = <1>;
};
};
@@ -100,16 +100,10 @@
nvmem-cell-names = "mac-address";
};
&factory {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
macaddr_factory_1f100: macaddr@1f100 {
compatible = "mac-base";
reg = <0x1f100 0x6>;
#nvmem-cell-cells = <1>;
};
&nvmem {
macaddr_factory_1f100: macaddr@1f100 {
compatible = "mac-base";
reg = <0x1f100 0x6>;
#nvmem-cell-cells = <1>;
};
};
@@ -100,16 +100,10 @@
nvmem-cell-names = "mac-address";
};
&factory {
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
macaddr_factory_1f100: macaddr@1f100 {
compatible = "mac-base";
reg = <0x1f100 0x6>;
#nvmem-cell-cells = <1>;
};
&nvmem {
macaddr_factory_1f100: macaddr@1f100 {
compatible = "mac-base";
reg = <0x1f100 0x6>;
#nvmem-cell-cells = <1>;
};
};
@@ -100,7 +100,7 @@
read-only;
};
factory: partition@40000 {
partition@40000 {
label = "factory";
reg = <0x40000 0x10000>;
read-only;
@@ -57,7 +57,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
obj-$(CONFIG_KEYBOARD_TM2_TOUCHKEY) += tm2-touchkey.o
--- /dev/null
+++ b/drivers/input/keyboard/sx951x.c
@@ -0,0 +1,490 @@
@@ -0,0 +1,494 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Input driver for Semtech SX9512/SX9513 capacitive touch sensors.
@@ -237,11 +237,12 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
+}
+
+static int sx951x_led_init(struct sx951x_priv *priv,
+ struct device_node *channel_node, u32 reg)
+ struct fwnode_handle *channel_node, u32 reg)
+{
+ struct device_node *led_node;
+ struct fwnode_handle *led_node;
+ struct sx951x_led *led = &priv->leds[reg];
+ struct led_init_data init_data = {};
+ const char *name;
+ int error;
+
+ if (led->registered) {
@@ -250,7 +251,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
+ return -EINVAL;
+ }
+
+ led_node = of_get_child_by_name(channel_node, "led");
+ led_node = fwnode_get_named_child_node(channel_node, "led");
+ if (!led_node) {
+ /* No LED */
+ return 0;
@@ -261,8 +262,11 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
+ led->cdev.max_brightness = 1;
+ led->cdev.brightness = LED_OFF;
+
+ init_data.default_label = of_get_property(led_node, "label", NULL);
+ init_data.fwnode = of_fwnode_handle(led_node);
+ if (fwnode_property_read_string(led_node, "label", &name))
+ name = fwnode_get_name(led_node);
+
+ init_data.default_label = name;
+ init_data.fwnode = led_node;
+
+ led->reg = reg;
+ led->priv = priv;
@@ -298,7 +302,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
+ }
+}
+
+static int sx951x_channel_init(struct sx951x_priv *priv, struct device_node *of_node,
+static int sx951x_channel_init(struct sx951x_priv *priv, struct fwnode_handle *fw,
+ u32 chan_idx)
+{
+ struct sx951x_key_data *key_data;
@@ -311,14 +315,14 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
+ key_data->cin_delta = SX951X_KEY_DEFAULT_CIN_DELTA;
+ key_data->sense_threshold = SX951X_KEY_DEFAULT_SENSE_THRESHOLD;
+
+ error = of_property_read_u32(of_node, "linux,keycodes",
+ error = fwnode_property_read_u32(fw, "linux,keycodes",
+ &priv->keycodes[chan_idx]);
+ if (error) {
+ /* Not configured */
+ return 0;
+ }
+
+ error = of_property_read_u32(of_node, "semtech,cin-delta",
+ error = fwnode_property_read_u32(fw, "semtech,cin-delta",
+ &key_data->cin_delta);
+ if (key_data->cin_delta > 0x03) {
+ dev_err(dev, "Failed to read cin-delta for channel %d: %d\n",
@@ -326,7 +330,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
+ return error;
+ }
+
+ error = of_property_read_u32(of_node, "semtech,sense-threshold",
+ error = fwnode_property_read_u32(fw, "semtech,sense-threshold",
+ &key_data->sense_threshold);
+ if (key_data->sense_threshold > 0xff) {
+ dev_err(dev, "Failed to read sense-threshold for channel %d: %d\n",
@@ -364,8 +368,8 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
+ int error;
+ u32 reg;
+
+ for_each_child_of_node_scoped(dev->of_node, child) {
+ error = of_property_read_u32(child, "reg", &reg);
+ device_for_each_child_node_scoped(dev, child) {
+ error = fwnode_property_read_u32(child, "reg", &reg);
+ if (error != 0 || reg >= SX951X_NUM_CHANNELS) {
+ dev_err(dev, "Invalid channel %d\n", reg);
+ return -EINVAL;
@@ -479,7 +483,7 @@ Signed-off-by: David Bauer <mail@david-bauer.net>
+ return PTR_ERR(priv->regmap);
+
+ /* Parse device configuration */
+ if (of_property_read_u32(dev->of_node, "poll-interval",
+ if (device_property_read_u32(dev, "poll-interval",
+ &priv->poll_interval))
+ priv->poll_interval = SX951X_POLL_INTERVAL;
+
@@ -34,7 +34,7 @@
compatible = "linux,ubi";
volumes {
factory: ubi-volume-factory {
ubi-volume-factory {
volname = "factory";
nvmem-layout {
@@ -29,7 +29,7 @@
read-only;
};
factory: partition@90000 {
partition@90000 {
label = "factory";
reg = <0x90000 0x10000>;
+2 -2
View File
@@ -9,10 +9,10 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=expat
PKG_CPE_ID:=cpe:/a:libexpat_project:libexpat
PKG_VERSION:=2.7.5
PKG_VERSION:=2.8.2
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_HASH:=9931f9860d18e6cf72d183eb8f309bfb96196c00e1d40caa978e95bc9aa978b6
PKG_HASH:=ef7d1994f533c9e7343d6c19f31064fc8ebbcbcaa144be3812b4f43052a05f4c
PKG_SOURCE_URL:=https://github.com/libexpat/libexpat/releases/download/R_$(subst .,_,$(PKG_VERSION))
HOST_BUILD_PARALLEL:=1