diff options
-rw-r--r-- | drivers/net/wireless/realtek/rtw89/core.c | 6 | ||||
-rw-r--r-- | drivers/net/wireless/realtek/rtw89/fw.c | 98 | ||||
-rw-r--r-- | drivers/net/wireless/realtek/rtw89/fw.h | 31 |
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, |