* [PATCH rtw-next 0/6] wifi: rtw89: fw: reduce memory use of firmware, and support board file
@ 2026-09-23 5:44 Ping-Ke Shih
2026-09-23 5:45 ` [PATCH rtw-next 1/6] wifi: rtw89: fw: duplicate recognized firmware elements Ping-Ke Shih
` (5 more replies)
0 siblings, 6 replies; 8+ messages in thread
From: Ping-Ke Shih @ 2026-09-23 5:44 UTC (permalink / raw)
To: linux-wireless; +Cc: kevin_yang
The first three patches aim to reduce memory use of firmware, because
RTL8922D has many variants and each of them uses different firmware.
Therefore, firmware binary file becomes large, but actually only need
one copy to specific variant. Duplicate the necessary part and persistent
them, and free memory allocated by request_firmware() after device
probes done.
The other three patches are to define TX power values (for now) by an
external a board file corresponding to a specific board (with specific
VID/PID). This is necessary because TX power values depends on board
(module) design, not on chip only. Add this as a initial work to make it
possible to record settings for customized module (not standard PCIE
module).
Ping-Ke Shih (3):
wifi: rtw89: fw: duplicate recognized firmware elements
wifi: rtw89: fw: release firmware after recognizing firmware elements
wifi: rtw89: fw: duplicate firmware suit data by vmalloc to reduce
memory fragment
Zong-Zhe Yang (3):
wifi: rtw89: led: check if board variant assigns led desc
wifi: rtw89: support variants of FW elements
wifi: rtw89: 8852cu: take board ID 1 for FW elements 28de/2432
drivers/net/wireless/realtek/rtw89/core.c | 6 +
drivers/net/wireless/realtek/rtw89/core.h | 19 ++
drivers/net/wireless/realtek/rtw89/fw.c | 252 +++++++++++++++---
drivers/net/wireless/realtek/rtw89/fw.h | 15 ++
drivers/net/wireless/realtek/rtw89/led.c | 2 +-
.../net/wireless/realtek/rtw89/rtw8852cu.c | 1 +
6 files changed, 253 insertions(+), 42 deletions(-)
base-commit: 2f77c66694108785a1f6ae6c5de06453e7f7a91f
--
2.25.1
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH rtw-next 1/6] wifi: rtw89: fw: duplicate recognized firmware elements
2026-09-23 5:44 [PATCH rtw-next 0/6] wifi: rtw89: fw: reduce memory use of firmware, and support board file Ping-Ke Shih
@ 2026-09-23 5:45 ` Ping-Ke Shih
2026-09-30 1:56 ` Ping-Ke Shih
2026-09-23 5:45 ` [PATCH rtw-next 2/6] wifi: rtw89: fw: release firmware after recognizing " Ping-Ke Shih
` (4 subsequent siblings)
5 siblings, 1 reply; 8+ messages in thread
From: Ping-Ke Shih @ 2026-09-23 5:45 UTC (permalink / raw)
To: linux-wireless; +Cc: kevin_yang
The firmware file contains variant elements for variant hardware, and
currently whole firmware content is persistent. However, RTL8922DE has many
variants and firmware file become grown to over 4MB, but actually size of
necessary duplicate elements are about 1.0MB.
To reduce memory usage, duplicate recognized firmware elements as
persistent memory, and will release firmware by next coming patch.
However, firmware for early chips doesn't have many variant elements, and
no need to duplicate memory for the purpose, so do it only if firmware
size is over 2MB.
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
drivers/net/wireless/realtek/rtw89/core.h | 1 +
drivers/net/wireless/realtek/rtw89/fw.c | 72 ++++++++++++++++++-----
2 files changed, 59 insertions(+), 14 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 3a6099386b09..4b4500a7a068 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -6094,6 +6094,7 @@ struct rtw89_fw_suit {
struct rtw89_fw_req_info {
const struct firmware *firmware;
struct completion completion;
+ bool free_after_probe;
};
struct rtw89_fw_log {
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 535cc2ad8611..3cdfb141f075 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -137,6 +137,33 @@ int rtw89_fw_check_rdy(struct rtw89_dev *rtwdev, enum rtw89_fwdl_check_type type
return 0;
}
+static const void *__rtw89_fw_elem_dup_if_needed(struct rtw89_dev *rtwdev,
+ const void *src, size_t len)
+{
+ struct rtw89_fw_req_info *fw_req = &rtwdev->fw.req;
+ const void *dup;
+
+ if (!fw_req->free_after_probe)
+ return src;
+
+ dup = devm_kmemdup(rtwdev->dev, src, len, GFP_KERNEL);
+ if (!dup) {
+ /* If failed to memdup, fallback to point to firmware->data. */
+ fw_req->free_after_probe = false;
+ return src;
+ }
+
+ return dup;
+}
+
+static const void *rtw89_fw_elem_dup_if_needed(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_element_hdr *elm)
+{
+ size_t len = sizeof(*elm) + le32_to_cpu(elm->size);
+
+ return __rtw89_fw_elem_dup_if_needed(rtwdev, elm, len);
+}
+
static int rtw89_fw_hdr_parser_v0(struct rtw89_dev *rtwdev, const u8 *fw, u32 len,
struct rtw89_fw_bin_info *info)
{
@@ -641,6 +668,8 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
const struct rtw89_mfw_hdr *mfw_hdr;
const u8 *mfw = firmware->data;
u32 mfw_len = firmware->size;
+ const u8 *mfw_info_ptr;
+ u32 mfw_info_size;
int ret;
int i;
@@ -650,8 +679,9 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
/* legacy firmware support normal type only */
if (type != RTW89_FW_NORMAL)
return -EINVAL;
- fw_suit->data = mfw;
fw_suit->size = mfw_len;
+ fw_suit->data = __rtw89_fw_elem_dup_if_needed(rtwdev, mfw,
+ fw_suit->size);
return 0;
}
@@ -686,14 +716,18 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
return -ENOENT;
found:
- fw_suit->data = mfw + le32_to_cpu(mfw_info->shift);
- fw_suit->size = le32_to_cpu(mfw_info->size);
+ mfw_info_ptr = mfw + le32_to_cpu(mfw_info->shift);
+ mfw_info_size = le32_to_cpu(mfw_info->size);
- if (fw_suit->data + fw_suit->size > mfw + mfw_len) {
+ if (mfw_info_ptr + mfw_info_size > mfw + mfw_len) {
rtw89_err(rtwdev, "fw_suit %d out of address\n", type);
return -EFAULT;
}
+ fw_suit->size = mfw_info_size;
+ fw_suit->data = __rtw89_fw_elem_dup_if_needed(rtwdev, mfw_info_ptr,
+ fw_suit->size);
+
return 0;
}
@@ -831,8 +865,9 @@ int __rtw89_fw_recognize_from_elm(struct rtw89_dev *rtwdev,
if (fw_suit->data)
return 1; /* ignore this element (a firmware is taken already) */
- fw_suit->data = elm->u.bbmcu.contents;
fw_suit->size = le32_to_cpu(elm->size);
+ fw_suit->data = __rtw89_fw_elem_dup_if_needed(rtwdev, elm->u.bbmcu.contents,
+ fw_suit->size);
return rtw89_fw_update_ver(rtwdev, type, fw_suit);
}
@@ -1252,7 +1287,12 @@ int rtw89_fw_recognize_txpwr_from_elm(struct rtw89_dev *rtwdev,
conf->rfe_type = txpwr_elm->rfe_type;
conf->ent_sz = txpwr_elm->ent_sz;
conf->num_ents = le32_to_cpu(txpwr_elm->num_ents);
+ /*
+ * The conf->data is used by rtw89_core_setup_rfe_parms() to do format
+ * conversion before releasing firmware. No need to duplicate.
+ */
conf->data = txpwr_elm->content;
+
return 0;
}
@@ -1263,6 +1303,7 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev,
{
struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
const struct rtw89_chip_info *chip = rtwdev->chip;
+ const struct rtw89_fw_element_hdr *elm_dup;
struct rtw89_hal *hal = &rtwdev->hal;
u16 aid = le16_to_cpu(elm->aid);
u32 needed_bitmap = 0;
@@ -1293,6 +1334,8 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev,
if (!elm_info->txpwr_trk)
return -ENOMEM;
+ elm_dup = rtw89_fw_elem_dup_if_needed(rtwdev, elm);
+
for (type = 0; bitmap; type++, bitmap >>= 1) {
if (!(bitmap & BIT(0)))
continue;
@@ -1309,10 +1352,10 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev,
else
break;
- elm_info->txpwr_trk->delta[type] = &elm->u.txpwr_trk.contents[offset];
+ elm_info->txpwr_trk->delta[type] = &elm_dup->u.txpwr_trk.contents[offset];
offset += subband;
- if (offset * DELTA_SWINGIDX_SIZE > le32_to_cpu(elm->size))
+ if (offset * DELTA_SWINGIDX_SIZE > le32_to_cpu(elm_dup->size))
goto err;
}
@@ -1347,7 +1390,7 @@ int rtw89_build_rfk_log_fmt_from_elm(struct rtw89_dev *rtwdev,
if (rfk_id >= RTW89_PHY_C2H_RFK_LOG_FUNC_NUM)
return 1;
- elm_info->rfk_log_fmt->elm[rfk_id] = elm;
+ elm_info->rfk_log_fmt->elm[rfk_id] = rtw89_fw_elem_dup_if_needed(rtwdev, elm);
return 0;
}
@@ -1454,7 +1497,7 @@ int rtw89_build_afe_pwr_seq_from_elm(struct rtw89_dev *rtwdev,
{
struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
- elm_info->afe = elm;
+ elm_info->afe = rtw89_fw_elem_dup_if_needed(rtwdev, elm);
return 0;
}
@@ -1466,7 +1509,7 @@ int rtw89_recognize_diag_mac_from_elm(struct rtw89_dev *rtwdev,
{
struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
- elm_info->diag_mac = elm;
+ elm_info->diag_mac = rtw89_fw_elem_dup_if_needed(rtwdev, elm);
return 0;
}
@@ -1492,7 +1535,7 @@ int rtw89_build_tx_comp_from_elm(struct rtw89_dev *rtwdev,
else if (elm_info->tx_comp)
return 1; /* ignore if an element is existing */
- elm_info->tx_comp = elm;
+ elm_info->tx_comp = rtw89_fw_elem_dup_if_needed(rtwdev, elm);
return 0;
}
@@ -2106,13 +2149,12 @@ static int rtw89_load_firmware_req(struct rtw89_dev *rtwdev,
struct rtw89_fw_req_info *req,
const char *fw_name, bool nowarn)
{
- int ret;
+ int ret = 0;
if (req->firmware) {
rtw89_debug(rtwdev, RTW89_DBG_FW,
"full firmware has been early requested\n");
- complete_all(&req->completion);
- return 0;
+ goto out;
}
if (nowarn)
@@ -2120,6 +2162,8 @@ static int rtw89_load_firmware_req(struct rtw89_dev *rtwdev,
else
ret = request_firmware(&req->firmware, fw_name, rtwdev->dev);
+out:
+ req->free_after_probe = req->firmware && req->firmware->size > 0x200000;
complete_all(&req->completion);
return ret;
--
2.25.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH rtw-next 2/6] wifi: rtw89: fw: release firmware after recognizing firmware elements
2026-09-23 5:44 [PATCH rtw-next 0/6] wifi: rtw89: fw: reduce memory use of firmware, and support board file Ping-Ke Shih
2026-09-23 5:45 ` [PATCH rtw-next 1/6] wifi: rtw89: fw: duplicate recognized firmware elements Ping-Ke Shih
@ 2026-09-23 5:45 ` Ping-Ke Shih
2026-09-23 5:45 ` [PATCH rtw-next 3/6] wifi: rtw89: fw: duplicate firmware suit data by vmalloc to reduce memory fragment Ping-Ke Shih
` (3 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Ping-Ke Shih @ 2026-09-23 5:45 UTC (permalink / raw)
To: linux-wireless; +Cc: kevin_yang
The firmware elements are recognized according to hardware variants in
rtw89_chip_info_setup(), and the last function uses firmware content is
rtw89_core_setup_rfe_parms(). Release firmware at the end of the function
if we have duplicated the elements.
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
drivers/net/wireless/realtek/rtw89/core.c | 4 ++++
drivers/net/wireless/realtek/rtw89/fw.c | 24 ++++++++++++++---------
drivers/net/wireless/realtek/rtw89/fw.h | 1 +
3 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 0c1c5c8ae8ce..5ca0c401c43a 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -7388,6 +7388,7 @@ void rtw89_core_rfkill_poll(struct rtw89_dev *rtwdev, bool force)
int rtw89_chip_info_setup(struct rtw89_dev *rtwdev)
{
+ struct rtw89_fw_req_info *fw_req = &rtwdev->fw.req;
struct rtw89_efuse *efuse = &rtwdev->efuse;
struct rtw89_hal *hal = &rtwdev->hal;
int ret;
@@ -7437,6 +7438,9 @@ int rtw89_chip_info_setup(struct rtw89_dev *rtwdev)
hal->cid, hal->cv, hal->aid, hal->acv, efuse->rfe_type);
out:
+ if (fw_req->free_after_probe)
+ __rtw89_unload_firmware(rtwdev);
+
rtw89_mac_pwr_off(rtwdev);
return ret;
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 3cdfb141f075..e4b59690622d 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -2206,20 +2206,26 @@ static void rtw89_unload_firmware_elements(struct rtw89_dev *rtwdev)
kfree(elm_info->rfk_log_fmt);
}
-void rtw89_unload_firmware(struct rtw89_dev *rtwdev)
+void __rtw89_unload_firmware(struct rtw89_dev *rtwdev)
{
struct rtw89_fw_info *fw = &rtwdev->fw;
- cancel_work_sync(&rtwdev->load_firmware_work);
+ release_firmware(fw->req.firmware);
- if (fw->req.firmware) {
- release_firmware(fw->req.firmware);
+ /*
+ * Directly call this to free firmware early in normal flow. Assign
+ * NULL back in case rtw89_free_ieee80211_hw() or tw89_core_deinit()
+ * try to release the same one again in error handling paths.
+ */
+ fw->req.firmware = NULL;
+}
- /* assign NULL back in case rtw89_free_ieee80211_hw()
- * try to release the same one again.
- */
- fw->req.firmware = NULL;
- }
+void rtw89_unload_firmware(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_fw_info *fw = &rtwdev->fw;
+
+ cancel_work_sync(&rtwdev->load_firmware_work);
+ __rtw89_unload_firmware(rtwdev);
kfree(fw->log.fmts);
rtw89_unload_firmware_elements(rtwdev);
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index 473278f6bbea..cb4209df5843 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -5419,6 +5419,7 @@ rtw89_early_fw_feature_recognize(struct device *device,
int rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
bool include_bb);
void rtw89_load_firmware_work(struct work_struct *work);
+void __rtw89_unload_firmware(struct rtw89_dev *rtwdev);
void rtw89_unload_firmware(struct rtw89_dev *rtwdev);
int rtw89_wait_firmware_completion(struct rtw89_dev *rtwdev);
int rtw89_fw_log_prepare(struct rtw89_dev *rtwdev);
--
2.25.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH rtw-next 3/6] wifi: rtw89: fw: duplicate firmware suit data by vmalloc to reduce memory fragment
2026-09-23 5:44 [PATCH rtw-next 0/6] wifi: rtw89: fw: reduce memory use of firmware, and support board file Ping-Ke Shih
2026-09-23 5:45 ` [PATCH rtw-next 1/6] wifi: rtw89: fw: duplicate recognized firmware elements Ping-Ke Shih
2026-09-23 5:45 ` [PATCH rtw-next 2/6] wifi: rtw89: fw: release firmware after recognizing " Ping-Ke Shih
@ 2026-09-23 5:45 ` Ping-Ke Shih
2026-09-23 5:45 ` [PATCH rtw-next 4/6] wifi: rtw89: led: check if board variant assigns led desc Ping-Ke Shih
` (2 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Ping-Ke Shih @ 2026-09-23 5:45 UTC (permalink / raw)
To: linux-wireless; +Cc: kevin_yang
The firmware suit data might be just over 512kB (e.g. 540kB) and actual
allocation size of devm_kmemdup() can be 1MB, which is a big internal
fragment. Use vmalooc() instead to reduce fragment.
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
drivers/net/wireless/realtek/rtw89/core.c | 1 +
drivers/net/wireless/realtek/rtw89/core.h | 3 ++
drivers/net/wireless/realtek/rtw89/fw.c | 52 ++++++++++++++++++-----
3 files changed, 46 insertions(+), 10 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 5ca0c401c43a..e61d7e89882f 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -6986,6 +6986,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)
}
INIT_LIST_HEAD(&rtwdev->scan_info.chan_list);
INIT_LIST_HEAD(&rtwdev->tx_waits);
+ INIT_LIST_HEAD(&rtwdev->fw.dup_data_list);
INIT_WORK(&rtwdev->ba_work, rtw89_core_ba_work);
INIT_WORK(&rtwdev->txq_work, rtw89_core_txq_work);
INIT_DELAYED_WORK(&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work);
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 4b4500a7a068..d8adf7425a3a 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -6059,6 +6059,7 @@ enum rtw89_fw_feature {
struct rtw89_fw_suit {
enum rtw89_fw_type type;
const u8 *data;
+ struct list_head list;
u32 size;
u8 major_ver;
u8 minor_ver;
@@ -6150,6 +6151,8 @@ struct rtw89_fw_info {
struct rtw89_fw_elm_info elm_info;
struct rtw89_fw_secure sec;
+ struct list_head dup_data_list;
+
DECLARE_BITMAP(feature_map, NUM_OF_RTW89_FW_FEATURES);
};
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index e4b59690622d..7c7c0a4f703e 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -3,6 +3,7 @@
*/
#include <linux/if_arp.h>
+#include <linux/vmalloc.h>
#include "cam.h"
#include "chan.h"
#include "coex.h"
@@ -138,15 +139,23 @@ int rtw89_fw_check_rdy(struct rtw89_dev *rtwdev, enum rtw89_fwdl_check_type type
}
static const void *__rtw89_fw_elem_dup_if_needed(struct rtw89_dev *rtwdev,
- const void *src, size_t len)
+ const void *src, size_t len,
+ bool is_fw_suit)
{
struct rtw89_fw_req_info *fw_req = &rtwdev->fw.req;
- const void *dup;
+ void *dup;
if (!fw_req->free_after_probe)
return src;
- dup = devm_kmemdup(rtwdev->dev, src, len, GFP_KERNEL);
+ if (is_fw_suit) {
+ dup = vmalloc(len);
+ if (dup)
+ memcpy(dup, src, len);
+ } else {
+ dup = devm_kmemdup(rtwdev->dev, src, len, GFP_KERNEL);
+ }
+
if (!dup) {
/* If failed to memdup, fallback to point to firmware->data. */
fw_req->free_after_probe = false;
@@ -161,7 +170,22 @@ static const void *rtw89_fw_elem_dup_if_needed(struct rtw89_dev *rtwdev,
{
size_t len = sizeof(*elm) + le32_to_cpu(elm->size);
- return __rtw89_fw_elem_dup_if_needed(rtwdev, elm, len);
+ return __rtw89_fw_elem_dup_if_needed(rtwdev, elm, len, false);
+}
+
+static const void *rtw89_fw_suit_data_dup_if_needed(struct rtw89_dev *rtwdev,
+ struct rtw89_fw_suit *fw_suit,
+ const void *src)
+{
+ struct rtw89_fw_info *fw = &rtwdev->fw;
+ const void *dup;
+
+ dup = __rtw89_fw_elem_dup_if_needed(rtwdev, src, fw_suit->size, true);
+
+ if (dup != src)
+ list_add_tail(&fw_suit->list, &fw->dup_data_list);
+
+ return dup;
}
static int rtw89_fw_hdr_parser_v0(struct rtw89_dev *rtwdev, const u8 *fw, u32 len,
@@ -680,8 +704,7 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
if (type != RTW89_FW_NORMAL)
return -EINVAL;
fw_suit->size = mfw_len;
- fw_suit->data = __rtw89_fw_elem_dup_if_needed(rtwdev, mfw,
- fw_suit->size);
+ fw_suit->data = rtw89_fw_suit_data_dup_if_needed(rtwdev, fw_suit, mfw);
return 0;
}
@@ -725,8 +748,7 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
}
fw_suit->size = mfw_info_size;
- fw_suit->data = __rtw89_fw_elem_dup_if_needed(rtwdev, mfw_info_ptr,
- fw_suit->size);
+ fw_suit->data = rtw89_fw_suit_data_dup_if_needed(rtwdev, fw_suit, mfw_info_ptr);
return 0;
}
@@ -866,8 +888,8 @@ int __rtw89_fw_recognize_from_elm(struct rtw89_dev *rtwdev,
return 1; /* ignore this element (a firmware is taken already) */
fw_suit->size = le32_to_cpu(elm->size);
- fw_suit->data = __rtw89_fw_elem_dup_if_needed(rtwdev, elm->u.bbmcu.contents,
- fw_suit->size);
+ fw_suit->data =
+ rtw89_fw_suit_data_dup_if_needed(rtwdev, fw_suit, elm->u.bbmcu.contents);
return rtw89_fw_update_ver(rtwdev, type, fw_suit);
}
@@ -2206,6 +2228,15 @@ static void rtw89_unload_firmware_elements(struct rtw89_dev *rtwdev)
kfree(elm_info->rfk_log_fmt);
}
+static void rtw89_unload_firmware_dup_data(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_fw_info *fw = &rtwdev->fw;
+ struct rtw89_fw_suit *fw_suit;
+
+ list_for_each_entry(fw_suit, &fw->dup_data_list, list)
+ vfree(fw_suit->data);
+}
+
void __rtw89_unload_firmware(struct rtw89_dev *rtwdev)
{
struct rtw89_fw_info *fw = &rtwdev->fw;
@@ -2229,6 +2260,7 @@ void rtw89_unload_firmware(struct rtw89_dev *rtwdev)
kfree(fw->log.fmts);
rtw89_unload_firmware_elements(rtwdev);
+ rtw89_unload_firmware_dup_data(rtwdev);
}
static u32 rtw89_fw_log_get_fmt_idx(struct rtw89_dev *rtwdev, u32 fmt_id)
--
2.25.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH rtw-next 4/6] wifi: rtw89: led: check if board variant assigns led desc
2026-09-23 5:44 [PATCH rtw-next 0/6] wifi: rtw89: fw: reduce memory use of firmware, and support board file Ping-Ke Shih
` (2 preceding siblings ...)
2026-09-23 5:45 ` [PATCH rtw-next 3/6] wifi: rtw89: fw: duplicate firmware suit data by vmalloc to reduce memory fragment Ping-Ke Shih
@ 2026-09-23 5:45 ` Ping-Ke Shih
2026-09-23 5:45 ` [PATCH rtw-next 5/6] wifi: rtw89: support variants of FW elements Ping-Ke Shih
2026-09-23 5:45 ` [PATCH rtw-next 6/6] wifi: rtw89: 8852cu: take board ID 1 for FW elements 28de/2432 Ping-Ke Shih
5 siblings, 0 replies; 8+ messages in thread
From: Ping-Ke Shih @ 2026-09-23 5:45 UTC (permalink / raw)
To: linux-wireless; +Cc: kevin_yang
From: Zong-Zhe Yang <kevin_yang@realtek.com>
Refine the check logic. The led initializer assumed a board variant must
have led desc, but board variant is going to add a new field, board_id,
for custom FW elements and not all board variants which require custom
FW elements have led desc.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
drivers/net/wireless/realtek/rtw89/led.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/realtek/rtw89/led.c b/drivers/net/wireless/realtek/rtw89/led.c
index ae74b7075d7c..18bfe626ef50 100644
--- a/drivers/net/wireless/realtek/rtw89/led.c
+++ b/drivers/net/wireless/realtek/rtw89/led.c
@@ -104,7 +104,7 @@ void rtw89_led_init(struct rtw89_dev *rtwdev)
/* single-GPIO monochrome LED is the only supported layout */
BUILD_BUG_ON(ARRAY_SIZE(rtw89_common_led_gpios) != 1);
- if (board)
+ if (board && board->led_desc)
desc = board->led_desc;
if (!desc->n_gpio || desc->n_gpio > RTW89_LED_MAX_NUM)
return;
--
2.25.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH rtw-next 5/6] wifi: rtw89: support variants of FW elements
2026-09-23 5:44 [PATCH rtw-next 0/6] wifi: rtw89: fw: reduce memory use of firmware, and support board file Ping-Ke Shih
` (3 preceding siblings ...)
2026-09-23 5:45 ` [PATCH rtw-next 4/6] wifi: rtw89: led: check if board variant assigns led desc Ping-Ke Shih
@ 2026-09-23 5:45 ` Ping-Ke Shih
2026-09-23 5:45 ` [PATCH rtw-next 6/6] wifi: rtw89: 8852cu: take board ID 1 for FW elements 28de/2432 Ping-Ke Shih
5 siblings, 0 replies; 8+ messages in thread
From: Ping-Ke Shih @ 2026-09-23 5:45 UTC (permalink / raw)
To: linux-wireless; +Cc: kevin_yang
From: Zong-Zhe Yang <kevin_yang@realtek.com>
Although there is a set of general FW elements parameters for each
chip, some chipsets depending on products may need variants of FW
elements for some specific adjustments.
Each of these chipsets will be defined with a specific VID/PID that
mapping to a specific board ID for FW elements. When probing these
chipsets, try to load rtw89/board_elm.bin and seek the given variant
ID. If found, apply the variant of FW elements. Otherwise, fallback
to the general FW elements.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
drivers/net/wireless/realtek/rtw89/core.c | 1 +
drivers/net/wireless/realtek/rtw89/core.h | 14 +++
drivers/net/wireless/realtek/rtw89/fw.c | 124 ++++++++++++++++++----
drivers/net/wireless/realtek/rtw89/fw.h | 14 +++
4 files changed, 135 insertions(+), 18 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index e61d7e89882f..2a1f54e9664a 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -7745,6 +7745,7 @@ void rtw89_free_ieee80211_hw(struct rtw89_dev *rtwdev)
}
EXPORT_SYMBOL(rtw89_free_ieee80211_hw);
+MODULE_FIRMWARE(RTW89_FWNAME_BOARD_ELM);
MODULE_AUTHOR("Realtek Corporation");
MODULE_DESCRIPTION("Realtek 802.11ax wireless core module");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index d8adf7425a3a..bcb58a9392cc 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -5302,6 +5302,8 @@ struct rtw89_reg_imr {
#define RTW89_GEN_MODULE_FWNAME(basename, maxformat) \
basename RTW89_GEN_MODULE_FWNAME_FMT(maxformat) ".bin"
+#define RTW89_FWNAME_BOARD_ELM "rtw89/board_elm.bin"
+
struct rtw89_fw_def {
const char *fw_basename;
u8 fw_format_max;
@@ -5893,7 +5895,18 @@ struct rtw89_chip_variant {
const struct rtw89_qta_def *qta_def_override;
};
+/* @pid can have extra suffix to describe the variant if necessary */
+#define RTW89_BOARD_ID(vid, pid) \
+ RTW89_BOARD_ID_##vid##_##pid
+
+enum rtw89_board_id {
+ RTW89_BOARD_ID(0000, 0000) = 0, /* reserve 0 for non-variant */
+
+ NUM_OF_RTW89_BOARD_IDS,
+};
+
struct rtw89_board_variant {
+ enum rtw89_board_id id;
const struct rtw89_led_desc *led_desc;
};
@@ -6094,6 +6107,7 @@ struct rtw89_fw_suit {
struct rtw89_fw_req_info {
const struct firmware *firmware;
+ const struct firmware *board_elm;
struct completion completion;
bool free_after_probe;
};
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 7c7c0a4f703e..d53e670a29cf 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -1658,30 +1658,17 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
},
};
-int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
+static int __rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev,
+ const struct firmware *firmware,
+ u32 offset, u32 skipped_elements,
+ u32 *unrecognized_elements)
{
- struct rtw89_fw_info *fw_info = &rtwdev->fw;
- const struct firmware *firmware = fw_info->req.firmware;
- const struct rtw89_chip_info *chip = rtwdev->chip;
- u32 unrecognized_elements = chip->needed_fw_elms;
const struct rtw89_fw_element_handler *handler;
const struct rtw89_fw_element_hdr *hdr;
- bool transition;
u32 elm_size;
u32 elem_id;
- u32 offset;
int ret;
- BUILD_BUG_ON(sizeof(chip->needed_fw_elms) * 8 < RTW89_FW_ELEMENT_ID_NUM);
-
- transition = !!((chip->needed_fw_elms & BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION)));
- unrecognized_elements &= ~BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION);
-
- offset = rtw89_mfw_get_size(rtwdev);
- offset = ALIGN(offset, RTW89_FW_ELEMENT_ALIGN);
- if (offset == 0)
- return -EINVAL;
-
while (offset + sizeof(*hdr) < firmware->size) {
hdr = (const struct rtw89_fw_element_hdr *)(firmware->data + offset);
@@ -1694,6 +1681,8 @@ int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
elem_id = le32_to_cpu(hdr->id);
if (elem_id >= ARRAY_SIZE(__fw_element_handlers))
goto next;
+ if (skipped_elements & BIT(elem_id))
+ goto next;
handler = &__fw_element_handlers[elem_id];
if (!handler->fn)
@@ -1709,12 +1698,105 @@ int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
rtw89_info(rtwdev, "Firmware element %s version: %4ph\n",
handler->name, hdr->ver);
- unrecognized_elements &= ~BIT(elem_id);
+ *unrecognized_elements &= ~BIT(elem_id);
next:
offset += sizeof(*hdr) + elm_size;
offset = ALIGN(offset, RTW89_FW_ELEMENT_ALIGN);
}
+ return 0;
+}
+
+static int rtw89_board_elm_seek(const struct firmware *board_elm,
+ enum rtw89_board_id board_id,
+ struct firmware *pseudo_fw)
+{
+ const struct rtw89_board_elm_hdr *hdr;
+ const struct rtw89_board_elm_ent *ent;
+ u32 ofst;
+ u32 size;
+ u16 num;
+
+ if (board_elm->size < sizeof(*hdr))
+ return -EINVAL;
+
+ hdr = (const struct rtw89_board_elm_hdr *)board_elm->data;
+ if (hdr->sig != RTW89_BOARD_ELM_SIG)
+ return -EINVAL;
+
+ BUILD_BUG_ON(U16_MAX < NUM_OF_RTW89_BOARD_IDS);
+
+ num = le16_to_cpu(hdr->num);
+ if (unlikely(board_elm->size < struct_size(hdr, ents, num)))
+ return -EFAULT;
+ if (num <= board_id)
+ return -ENOENT;
+
+ ent = &hdr->ents[board_id];
+
+ ofst = le32_to_cpu(ent->ofst);
+ size = le32_to_cpu(ent->size);
+
+ if (unlikely(board_elm->size < ofst + size))
+ return -EFAULT;
+
+ memset(pseudo_fw, 0, sizeof(*pseudo_fw));
+
+ pseudo_fw->data = (const void *)board_elm->data + ofst;
+ pseudo_fw->size = size;
+
+ return 0;
+}
+
+int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_board_variant *board = rtwdev->board;
+ struct rtw89_fw_req_info *fw_req = &rtwdev->fw.req;
+ const struct firmware *firmware = fw_req->firmware;
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ u32 unrecognized_elements = chip->needed_fw_elms;
+ u32 skipped_elements = 0;
+ bool transition;
+ u32 offset;
+ int ret;
+
+ BUILD_BUG_ON(sizeof(chip->needed_fw_elms) * 8 < RTW89_FW_ELEMENT_ID_NUM);
+ BUILD_BUG_ON(!__same_type(unrecognized_elements, chip->needed_fw_elms));
+ BUILD_BUG_ON(!__same_type(skipped_elements, chip->needed_fw_elms));
+
+ transition = !!((chip->needed_fw_elms & BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION)));
+ unrecognized_elements &= ~BIT(__RTW89_FW_ELEMENT_ID_INTL_TRANSITION);
+
+ if (fw_req->board_elm) {
+ struct firmware pseudo_fw;
+
+ ret = rtw89_board_elm_seek(fw_req->board_elm, board->id, &pseudo_fw);
+ if (ret)
+ goto mfw;
+
+ ret = __rtw89_fw_recognize_elements(rtwdev, &pseudo_fw, 0, 0,
+ &unrecognized_elements);
+ if (ret)
+ goto mfw;
+
+ skipped_elements = chip->needed_fw_elms & ~unrecognized_elements;
+
+ rtw89_debug(rtwdev, RTW89_DBG_FW, "apply elements 0x08%x from %s\n",
+ skipped_elements, RTW89_FWNAME_BOARD_ELM);
+ }
+
+mfw:
+ offset = rtw89_mfw_get_size(rtwdev);
+ offset = ALIGN(offset, RTW89_FW_ELEMENT_ALIGN);
+ if (offset == 0)
+ return -EINVAL;
+
+ ret = __rtw89_fw_recognize_elements(rtwdev, firmware, offset,
+ skipped_elements,
+ &unrecognized_elements);
+ if (ret)
+ return ret;
+
if (unrecognized_elements) {
if (transition) {
rtw89_info(rtwdev, "NOTE: This firmware is going to be obsolete!\n"
@@ -2171,6 +2253,7 @@ static int rtw89_load_firmware_req(struct rtw89_dev *rtwdev,
struct rtw89_fw_req_info *req,
const char *fw_name, bool nowarn)
{
+ const struct rtw89_board_variant *board = rtwdev->board;
int ret = 0;
if (req->firmware) {
@@ -2185,6 +2268,9 @@ static int rtw89_load_firmware_req(struct rtw89_dev *rtwdev,
ret = request_firmware(&req->firmware, fw_name, rtwdev->dev);
out:
+ if (board && board->id)
+ firmware_request_nowarn(&req->board_elm, RTW89_FWNAME_BOARD_ELM, rtwdev->dev);
+
req->free_after_probe = req->firmware && req->firmware->size > 0x200000;
complete_all(&req->completion);
@@ -2242,6 +2328,7 @@ void __rtw89_unload_firmware(struct rtw89_dev *rtwdev)
struct rtw89_fw_info *fw = &rtwdev->fw;
release_firmware(fw->req.firmware);
+ release_firmware(fw->req.board_elm);
/*
* Directly call this to free firmware early in normal flow. Assign
@@ -2249,6 +2336,7 @@ void __rtw89_unload_firmware(struct rtw89_dev *rtwdev)
* try to release the same one again in error handling paths.
*/
fw->req.firmware = NULL;
+ fw->req.board_elm = NULL;
}
void rtw89_unload_firmware(struct rtw89_dev *rtwdev)
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index cb4209df5843..23adf12f132e 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -4394,6 +4394,7 @@ struct rtw89_h2c_ofld {
#define RTW89_H2C_OFLD_W0_RX_TP GENMASK(27, 18)
#define RTW89_MFW_SIG 0xFF
+#define RTW89_BOARD_ELM_SIG 0xFE
struct rtw89_mfw_info {
u8 cv;
@@ -4419,6 +4420,19 @@ struct rtw89_mfw_hdr {
struct rtw89_mfw_info info[];
} __packed;
+struct rtw89_board_elm_ent {
+ u8 rsvd[8];
+ __le32 ofst; /* offset from beginning of rtw89_board_elm_hdr */
+ __le32 size;
+} __packed;
+
+struct rtw89_board_elm_hdr {
+ u8 sig; /* RTW89_BOARD_ELM_SIG */
+ u8 rsvd[13];
+ __le16 num;
+ struct rtw89_board_elm_ent ents[] __counted_by_le(num);
+} __packed;
+
struct rtw89_fw_logsuit_hdr {
__le32 rsvd;
__le32 count;
--
2.25.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH rtw-next 6/6] wifi: rtw89: 8852cu: take board ID 1 for FW elements 28de/2432
2026-09-23 5:44 [PATCH rtw-next 0/6] wifi: rtw89: fw: reduce memory use of firmware, and support board file Ping-Ke Shih
` (4 preceding siblings ...)
2026-09-23 5:45 ` [PATCH rtw-next 5/6] wifi: rtw89: support variants of FW elements Ping-Ke Shih
@ 2026-09-23 5:45 ` Ping-Ke Shih
5 siblings, 0 replies; 8+ messages in thread
From: Ping-Ke Shih @ 2026-09-23 5:45 UTC (permalink / raw)
To: linux-wireless; +Cc: kevin_yang
From: Zong-Zhe Yang <kevin_yang@realtek.com>
RTL8852CU with 28de/2432 prefers to use variant of some FW elements.
Use board ID 1 for these FW elements. And, configure board ID to it
correspondingly to try to load the variant.
Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
drivers/net/wireless/realtek/rtw89/core.h | 1 +
drivers/net/wireless/realtek/rtw89/rtw8852cu.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index bcb58a9392cc..5b26ea33f329 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -5901,6 +5901,7 @@ struct rtw89_chip_variant {
enum rtw89_board_id {
RTW89_BOARD_ID(0000, 0000) = 0, /* reserve 0 for non-variant */
+ RTW89_BOARD_ID(28de, 2432) = 1,
NUM_OF_RTW89_BOARD_IDS,
};
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
index 6f852f4a713e..2e46b05b05ee 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
@@ -89,6 +89,7 @@ static const struct rtw89_led_desc rtw8852cu_valve_led_desc = {
};
static const struct rtw89_board_variant rtw89_8852cu_valve_board = {
+ .id = RTW89_BOARD_ID(28de, 2432),
.led_desc = &rtw8852cu_valve_led_desc,
};
--
2.25.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH rtw-next 1/6] wifi: rtw89: fw: duplicate recognized firmware elements
2026-09-23 5:45 ` [PATCH rtw-next 1/6] wifi: rtw89: fw: duplicate recognized firmware elements Ping-Ke Shih
@ 2026-09-30 1:56 ` Ping-Ke Shih
0 siblings, 0 replies; 8+ messages in thread
From: Ping-Ke Shih @ 2026-09-30 1:56 UTC (permalink / raw)
To: Ping-Ke Shih, linux-wireless; +Cc: kevin_yang
Ping-Ke Shih <pkshih@realtek.com> wrote:
> The firmware file contains variant elements for variant hardware, and
> currently whole firmware content is persistent. However, RTL8922DE has many
> variants and firmware file become grown to over 4MB, but actually size of
> necessary duplicate elements are about 1.0MB.
>
> To reduce memory usage, duplicate recognized firmware elements as
> persistent memory, and will release firmware by next coming patch.
>
> However, firmware for early chips doesn't have many variant elements, and
> no need to duplicate memory for the purpose, so do it only if firmware
> size is over 2MB.
>
> Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
6 patch(es) applied to rtw-next branch of rtw.git, thanks.
504bbf01735f wifi: rtw89: fw: duplicate recognized firmware elements
f31a3a3afab1 wifi: rtw89: fw: release firmware after recognizing firmware elements
0ba6a2d26fef wifi: rtw89: fw: duplicate firmware suit data by vmalloc to reduce memory fragment
86e0056aaaf3 wifi: rtw89: led: check if board variant assigns led desc
af5905e22a43 wifi: rtw89: support variants of FW elements
e55ca4e813e5 wifi: rtw89: 8852cu: take board ID 1 for FW elements 28de/2432
---
https://github.com/pkshih/rtw.git
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-09-30 1:56 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-23 5:44 [PATCH rtw-next 0/6] wifi: rtw89: fw: reduce memory use of firmware, and support board file Ping-Ke Shih
2026-09-23 5:45 ` [PATCH rtw-next 1/6] wifi: rtw89: fw: duplicate recognized firmware elements Ping-Ke Shih
2026-09-30 1:56 ` Ping-Ke Shih
2026-09-23 5:45 ` [PATCH rtw-next 2/6] wifi: rtw89: fw: release firmware after recognizing " Ping-Ke Shih
2026-09-23 5:45 ` [PATCH rtw-next 3/6] wifi: rtw89: fw: duplicate firmware suit data by vmalloc to reduce memory fragment Ping-Ke Shih
2026-09-23 5:45 ` [PATCH rtw-next 4/6] wifi: rtw89: led: check if board variant assigns led desc Ping-Ke Shih
2026-09-23 5:45 ` [PATCH rtw-next 5/6] wifi: rtw89: support variants of FW elements Ping-Ke Shih
2026-09-23 5:45 ` [PATCH rtw-next 6/6] wifi: rtw89: 8852cu: take board ID 1 for FW elements 28de/2432 Ping-Ke Shih
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox