summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/realtek/rtw89/core.c6
-rw-r--r--drivers/net/wireless/realtek/rtw89/fw.c98
-rw-r--r--drivers/net/wireless/realtek/rtw89/fw.h31
3 files changed, 135 insertions, 0 deletions
diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index ecdd05b851b7..62c21c09cf92 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -3779,6 +3779,12 @@ int rtw89_chip_info_setup(struct rtw89_dev *rtwdev)
return ret;
}
+ ret = rtw89_fw_recognize_elements(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to recognize firmware elements\n");
+ return ret;
+ }
+
ret = rtw89_chip_efuse_info_setup(rtwdev);
if (ret)
return ret;
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index d8bcc51242ff..d36fbbb6c549 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -284,6 +284,26 @@ found:
return 0;
}
+static u32 rtw89_mfw_get_size(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_fw_info *fw_info = &rtwdev->fw;
+ const struct firmware *firmware = fw_info->req.firmware;
+ const struct rtw89_mfw_hdr *mfw_hdr =
+ (const struct rtw89_mfw_hdr *)firmware->data;
+ const struct rtw89_mfw_info *mfw_info;
+ u32 size;
+
+ if (mfw_hdr->sig != RTW89_MFW_SIG) {
+ rtw89_warn(rtwdev, "not mfw format\n");
+ return 0;
+ }
+
+ mfw_info = &mfw_hdr->info[mfw_hdr->fw_nr - 1];
+ size = le32_to_cpu(mfw_info->shift) + le32_to_cpu(mfw_info->size);
+
+ return size;
+}
+
static void rtw89_fw_update_ver_v0(struct rtw89_dev *rtwdev,
struct rtw89_fw_suit *fw_suit,
const struct rtw89_fw_hdr *hdr)
@@ -366,6 +386,21 @@ int __rtw89_fw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
return rtw89_fw_update_ver(rtwdev, type, fw_suit);
}
+static
+int __rtw89_fw_recognize_from_elm(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_element_hdr *elm,
+ const void *data)
+{
+ enum rtw89_fw_type type = (enum rtw89_fw_type)data;
+ struct rtw89_fw_suit *fw_suit;
+
+ fw_suit = rtw89_fw_suit_get(rtwdev, type);
+ fw_suit->data = elm->u.common.contents;
+ fw_suit->size = le32_to_cpu(elm->size);
+
+ return rtw89_fw_update_ver(rtwdev, type, fw_suit);
+}
+
#define __DEF_FW_FEAT_COND(__cond, __op) \
static bool __fw_feat_cond_ ## __cond(u32 suit_ver_code, u32 comp_ver_code) \
{ \
@@ -509,6 +544,69 @@ normal_done:
return 0;
}
+struct rtw89_fw_element_handler {
+ int (*fn)(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_element_hdr *elm, const void *data);
+ const void *data;
+ const char *name;
+};
+
+static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
+ [RTW89_FW_ELEMENT_ID_BBMCU0] = {__rtw89_fw_recognize_from_elm,
+ (const void *)RTW89_FW_BBMCU0, NULL},
+ [RTW89_FW_ELEMENT_ID_BBMCU1] = {__rtw89_fw_recognize_from_elm,
+ (const void *)RTW89_FW_BBMCU1, NULL},
+};
+
+int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_fw_info *fw_info = &rtwdev->fw;
+ const struct firmware *firmware = fw_info->req.firmware;
+ const struct rtw89_fw_element_handler *handler;
+ const struct rtw89_fw_element_hdr *hdr;
+ u32 elm_size;
+ u32 elem_id;
+ u32 offset;
+ int ret;
+
+ 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);
+
+ elm_size = le32_to_cpu(hdr->size);
+ if (offset + elm_size >= firmware->size) {
+ rtw89_warn(rtwdev, "firmware element size exceeds\n");
+ break;
+ }
+
+ elem_id = le32_to_cpu(hdr->id);
+ if (elem_id >= ARRAY_SIZE(__fw_element_handlers))
+ goto next;
+
+ handler = &__fw_element_handlers[elem_id];
+ if (!handler->fn)
+ goto next;
+
+ ret = handler->fn(rtwdev, hdr, handler->data);
+ if (ret)
+ return ret;
+
+ if (handler->name)
+ rtw89_info(rtwdev, "Firmware element %s version: %4ph\n",
+ handler->name, hdr->ver);
+
+next:
+ offset += sizeof(*hdr) + elm_size;
+ offset = ALIGN(offset, RTW89_FW_ELEMENT_ALIGN);
+ }
+
+ return 0;
+}
+
void rtw89_h2c_pkt_set_hdr(struct rtw89_dev *rtwdev, struct sk_buff *skb,
u8 type, u8 cat, u8 class, u8 func,
bool rack, bool dack, u32 len)
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index 8961958009dd..cb9eed8c2f1d 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -3400,6 +3400,36 @@ struct rtw89_fw_logsuit_hdr {
__le32 ids[];
} __packed;
+#define RTW89_FW_ELEMENT_ALIGN 16
+
+enum rtw89_fw_element_id {
+ RTW89_FW_ELEMENT_ID_BBMCU0 = 0,
+ RTW89_FW_ELEMENT_ID_BBMCU1 = 1,
+};
+
+struct rtw89_fw_element_hdr {
+ __le32 id; /* enum rtw89_fw_element_id */
+ __le32 size; /* exclude header size */
+ u8 ver[4];
+ __le32 rsvd0;
+ __le32 rsvd1;
+ __le32 rsvd2;
+ union {
+ struct {
+ u8 priv[8];
+ u8 contents[];
+ } __packed common;
+ struct {
+ u8 idx;
+ u8 rsvd[7];
+ struct {
+ __le32 addr;
+ __le32 data;
+ } __packed regs[];
+ } __packed reg2;
+ } __packed u;
+} __packed;
+
struct fwcmd_hdr {
__le32 hdr0;
__le32 hdr1;
@@ -3581,6 +3611,7 @@ struct rtw89_fw_h2c_rf_get_mccch {
int rtw89_fw_check_rdy(struct rtw89_dev *rtwdev);
int rtw89_fw_recognize(struct rtw89_dev *rtwdev);
+int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev);
const struct firmware *
rtw89_early_fw_feature_recognize(struct device *device,
const struct rtw89_chip_info *chip,