/****************************************************************************** * * Copyright(c) 2019 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * ******************************************************************************/ #include "fwdl.h" #define FWDL_WAIT_CNT 400000 #define FWDL_SECTION_MAX_NUM 10 #define FWDL_SECTION_CHKSUM_LEN 8 #define FWDL_SECTION_PER_PKT_LEN 2020 #define FWDL_TRY_CNT 3 #define FWDL_DIGEST_SIZE 0x20 struct fwhdr_section_info { u8 redl; u8 *addr; u32 len; u32 dladdr; }; struct fw_bin_info { u8 section_num; u32 hdr_len; u32 git_idx; struct fwhdr_section_info section_info[FWDL_SECTION_MAX_NUM]; }; struct hw_info { u8 chip; u8 cut; u8 category; }; struct fwld_info { u32 len; u8 *fw; }; static inline void fwhdr_section_parser(struct fwhdr_section_t *section, struct fwhdr_section_info *info) { u32 hdr_val; u32 section_len; hdr_val = le32_to_cpu(section->dword1); section_len = GET_FIELD(hdr_val, SECTION_INFO_SEC_SIZE); if (hdr_val & SECTION_INFO_CHECKSUM) section_len += FWDL_SECTION_CHKSUM_LEN; info->len = section_len; info->redl = (hdr_val & SECTION_INFO_REDL) ? 1 : 0; info->dladdr = (GET_FIELD(le32_to_cpu(section->dword0), SECTION_INFO_SEC_DL_ADDR)) & 0x1FFFFFFF; } static inline void fwhdr_hdr_parser(struct fwhdr_hdr_t *hdr, struct fw_bin_info *info) { u32 hdr_val; hdr_val = le32_to_cpu(hdr->dword6); info->section_num = GET_FIELD(hdr_val, FWHDR_SEC_NUM); info->hdr_len = FWHDR_HDR_LEN + info->section_num * FWHDR_SECTION_LEN; /* fill HALMAC information */ hdr_val = le32_to_cpu(hdr->dword7); hdr_val = SET_CLR_WORD(hdr_val, FWDL_SECTION_PER_PKT_LEN, FWHDR_FW_PART_SZ); hdr->dword7 = cpu_to_le32(hdr_val); hdr_val = le32_to_cpu(hdr->dword2); info->git_idx = GET_FIELD(hdr_val, FWHDR_COMMITID); } static u32 fwhdr_parser(struct mac_ax_adapter *adapter, u8 *fw, u32 len, struct fw_bin_info *info) { u32 i; u8 *fw_end = fw + len; u8 *bin_ptr; struct fwhdr_section_info *cur_section_info; if (!info) { PLTFM_MSG_ERR("[ERR]%s: *info = NULL\n", __func__); return MACNPTR; } else if (!fw) { PLTFM_MSG_ERR("[ERR]%s: *fw = NULL\n", __func__); return MACNOITEM; } else if (!len) { PLTFM_MSG_ERR("[ERR]%s: len = 0\n", __func__); return MACBUFSZ; } fwhdr_hdr_parser((struct fwhdr_hdr_t *)fw, info); bin_ptr = fw + info->hdr_len; /* jump to section header */ fw += FWHDR_HDR_LEN; cur_section_info = info->section_info; for (i = 0; i < info->section_num; i++) { fwhdr_section_parser((struct fwhdr_section_t *)fw, cur_section_info); cur_section_info->addr = bin_ptr; bin_ptr += cur_section_info->len; fw += FWHDR_SECTION_LEN; cur_section_info++; } if (fw_end != bin_ptr) { PLTFM_MSG_ERR("[ERR]%s: ", __func__); PLTFM_MSG_ERR("fw bin size != fw size in fwhdr\n"); return MACFWBIN; } return MACSUCCESS; } static inline u32 update_fw_ver(struct mac_ax_adapter *adapter, struct fwhdr_hdr_t *hdr) { u32 hdr_val; struct mac_ax_fw_info *info = &adapter->fw_info; hdr_val = le32_to_cpu(hdr->dword1); info->major_ver = GET_FIELD(hdr_val, FWHDR_MAJORVER); info->minor_ver = GET_FIELD(hdr_val, FWHDR_MINORVER); info->sub_ver = GET_FIELD(hdr_val, FWHDR_SUBVERSION); info->sub_idx = GET_FIELD(hdr_val, FWHDR_SUBINDEX); hdr_val = le32_to_cpu(hdr->dword5); info->build_year = GET_FIELD(hdr_val, FWHDR_YEAR); hdr_val = le32_to_cpu(hdr->dword4); info->build_mon = GET_FIELD(hdr_val, FWHDR_MONTH); info->build_date = GET_FIELD(hdr_val, FWHDR_DATE); info->build_hour = GET_FIELD(hdr_val, FWHDR_HOUR); info->build_min = GET_FIELD(hdr_val, FWHDR_MIN); info->h2c_seq = 0; info->rec_seq = 0; return MACSUCCESS; } static u32 __fwhdr_download(struct mac_ax_adapter *adapter, u8 *fw, u32 hdr_len, u8 redl) { u8 *buf; u32 ret = 0; #if MAC_AX_PHL_H2C struct rtw_h2c_pkt *h2cb; #else struct h2c_buf *h2cb; #endif h2cb = h2cb_alloc(adapter, H2CB_CLASS_DATA); if (!h2cb) { PLTFM_MSG_ERR("[ERR]%s: h2cb_alloc fail\n", __func__); return MACNPTR; } buf = h2cb_put(h2cb, hdr_len); if (!buf) { PLTFM_MSG_ERR("[ERR]%s: h2cb_put fail\n", __func__); ret = MACNOBUF; goto fail; } PLTFM_MEMCPY(buf, fw, hdr_len); if (redl) { ret = h2c_pkt_set_hdr_fwdl(adapter, h2cb, FWCMD_TYPE_H2C, FWCMD_H2C_CAT_MAC, FWCMD_H2C_CL_FWDL, FWCMD_H2C_FUNC_FWHDR_REDL, 0, 0); } else { ret = h2c_pkt_set_hdr_fwdl(adapter, h2cb, FWCMD_TYPE_H2C, FWCMD_H2C_CAT_MAC, FWCMD_H2C_CL_FWDL, FWCMD_H2C_FUNC_FWHDR_DL, 0, 0); } if (ret) { PLTFM_MSG_ERR("[ERR]%s: set h2c hdr fail\n", __func__); goto fail; } ret = h2c_pkt_build_txd(adapter, h2cb); if (ret) { PLTFM_MSG_ERR("[ERR]%s: build h2c txd fail\n", __func__); goto fail; } #if MAC_AX_PHL_H2C ret = PLTFM_TX(h2cb); #else ret = PLTFM_TX(h2cb->data, h2cb->len); #endif if (ret) { PLTFM_MSG_ERR("[ERR]%s: PLTFM_TX fail\n", __func__); goto fail; } h2cb_free(adapter, h2cb); return MACSUCCESS; fail: h2cb_free(adapter, h2cb); PLTFM_MSG_ERR("[ERR]%s ret: %d\n", __func__, ret); return ret; } #if MAC_AX_PHL_H2C static u32 __sections_build_txd(struct mac_ax_adapter *adapter, struct rtw_h2c_pkt *h2cb) { u8 *buf; u32 ret; u32 txd_len; struct rtw_t_meta_data info = {0}; struct mac_ax_ops *ops = adapter_to_mac_ops(adapter); info.type = RTW_PHL_PKT_TYPE_FWDL; info.pktlen = (u16)h2cb->data_len; txd_len = ops->txdesc_len(adapter, &info); buf = h2cb_push(h2cb, txd_len); if (!buf) { PLTFM_MSG_ERR("[ERR]%s: h2cb_push fail\n", __func__); return MACNPTR; } ret = ops->build_txdesc(adapter, &info, buf, txd_len); if (ret) { PLTFM_MSG_ERR("[ERR]%s: ", __func__); PLTFM_MSG_ERR("build_txdesc fail\n"); return ret; } return MACSUCCESS; } static u32 __sections_push(struct rtw_h2c_pkt *h2cb) { #define section_push_len 8 h2cb->vir_data -= section_push_len; h2cb->vir_tail -= section_push_len; return MACSUCCESS; } #else static u32 __sections_build_txd(struct mac_ax_adapter *adapter, struct h2c_buf *h2cb) { u8 *buf; u32 ret; u32 txd_len; struct rtw_t_meta_data info; struct mac_ax_ops *ops = adapter_to_mac_ops(adapter); info.type = RTW_PHL_PKT_TYPE_FWDL; info.pktlen = (u16)h2cb->len; txd_len = ops->txdesc_len(adapter, &info); buf = h2cb_push(h2cb, txd_len); if (!buf) { PLTFM_MSG_ERR("[ERR]%s: h2cb_push fail\n", __func__); return MACNPTR; } ret = ops->build_txdesc(adapter, &info, buf, txd_len); if (ret) { PLTFM_MSG_ERR("[ERR]%s: ", __func__); PLTFM_MSG_ERR("mac_build_txdesc fail\n"); return ret; } return MACSUCCESS; } #endif static u32 __sections_download(struct mac_ax_adapter *adapter, struct fwhdr_section_info *info) { u8 *section = info->addr; u32 residue_len = info->len; u32 pkt_len; u8 *buf; u32 ret = 0; #if MAC_AX_PHL_H2C struct rtw_h2c_pkt *h2cb; #else struct h2c_buf *h2cb; #endif while (residue_len) { if (residue_len >= FWDL_SECTION_PER_PKT_LEN) pkt_len = FWDL_SECTION_PER_PKT_LEN; else pkt_len = residue_len; h2cb = h2cb_alloc(adapter, H2CB_CLASS_LONG_DATA); if (!h2cb) { PLTFM_MSG_ERR("[ERR]%s: ", __func__); PLTFM_MSG_ERR("h2cb_alloc fail\n"); return MACNPTR; } #if MAC_AX_PHL_H2C __sections_push(h2cb); #endif buf = h2cb_put(h2cb, pkt_len); if (!buf) { PLTFM_MSG_ERR("[ERR]%s: ", __func__); PLTFM_MSG_ERR("h2cb_put fail\n"); ret = MACNOBUF; goto fail; } PLTFM_MEMCPY(buf, section, pkt_len); ret = __sections_build_txd(adapter, h2cb); if (ret) { PLTFM_MSG_ERR("[ERR]%s: ", __func__); PLTFM_MSG_ERR("__sections_build_txd fail\n"); goto fail; } #if MAC_AX_PHL_H2C ret = PLTFM_TX(h2cb); #else ret = PLTFM_TX(h2cb->data, h2cb->len); #endif if (ret) { PLTFM_MSG_ERR("[ERR]%s: PLTFM_TX fail\n", __func__); goto fail; } h2cb_free(adapter, h2cb); section += pkt_len; residue_len -= pkt_len; } return MACSUCCESS; fail: h2cb_free(adapter, h2cb); PLTFM_MSG_ERR("[ERR]%s ret: %d\n", __func__, ret); return ret; } static u32 __write_memory(struct mac_ax_adapter *adapter, u8 *buffer, u32 addr, u32 len) { struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); u8 *content = NULL; u32 dl_size; u32 target_addr, write_addr; u32 seg_size, seg_bytes; u32 val32; u32 index = 0; u32 ret = MACSUCCESS; PLTFM_MSG_WARN("%s ind access start\n", __func__); PLTFM_MUTEX_LOCK(&adapter->hw_info->ind_access_lock); adapter->hw_info->ind_aces_cnt++; MAC_REG_W32(R_AX_FILTER_MODEL_ADDR, addr); MAC_REG_W32(R_AX_INDIR_ACCESS_ENTRY, 0xAAAAAAAA); MAC_REG_W32(R_AX_INDIR_ACCESS_ENTRY + 4, 0xBBBBBBBB); val32 = MAC_REG_R32(R_AX_INDIR_ACCESS_ENTRY); if (val32 != 0xAAAAAAAA) { ret = MACMEMRO; goto ind_aces_end; } val32 = MAC_REG_R32(R_AX_INDIR_ACCESS_ENTRY + 4); if (val32 != 0xBBBBBBBB) { ret = MACMEMRO; goto ind_aces_end; } ind_aces_end: adapter->hw_info->ind_aces_cnt--; PLTFM_MUTEX_UNLOCK(&adapter->hw_info->ind_access_lock); PLTFM_MSG_WARN("%s ind access end\n", __func__); if (ret != MACSUCCESS) return ret; content = (u8 *)PLTFM_MALLOC(len); if (!content) { PLTFM_MSG_ERR("[ERR]%s: malloc fail\n", __func__); return MACNOBUF; } PLTFM_MEMCPY(content, buffer, len); dl_size = len; target_addr = addr; PLTFM_MSG_WARN("%s ind access trg 0x%X start\n", __func__, target_addr); PLTFM_MUTEX_LOCK(&adapter->hw_info->ind_access_lock); adapter->hw_info->ind_aces_cnt++; while (dl_size != 0) { MAC_REG_W32(R_AX_FILTER_MODEL_ADDR, target_addr); write_addr = R_AX_INDIR_ACCESS_ENTRY; if (dl_size >= ROMDL_SEG_LEN) seg_size = ROMDL_SEG_LEN; else seg_size = dl_size; seg_bytes = seg_size; while (seg_bytes != 0) { val32 = *((u32 *)(content + index)); MAC_REG_W32(write_addr, cpu_to_le32(val32)); seg_bytes -= 4; write_addr += 4; index += 4; } target_addr += seg_size; dl_size -= seg_size; } adapter->hw_info->ind_aces_cnt--; PLTFM_MUTEX_UNLOCK(&adapter->hw_info->ind_access_lock); PLTFM_MSG_WARN("%s ind access trg 0x%X end\n", __func__, target_addr); PLTFM_FREE(content, len); return MACSUCCESS; } static u32 fwdl_phase0(struct mac_ax_adapter *adapter) { u32 cnt = FWDL_WAIT_CNT; struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); if (adapter->sm.fwdl != MAC_AX_FWDL_CPU_ON) { PLTFM_MSG_ERR("[ERR]%s: state != CPU_ON\n", __func__); return MACPROCERR; } while (--cnt) { if (MAC_REG_R8(R_AX_WCPU_FW_CTRL) & B_AX_H2C_PATH_RDY) break; PLTFM_DELAY_US(1); } if (!cnt) { PLTFM_MSG_ERR("[ERR]%s: poll 0x1E0[1] = 1 fail\n", __func__); return MACPOLLTO; } adapter->sm.fwdl = MAC_AX_FWDL_H2C_PATH_RDY; return MACSUCCESS; } static u32 fwdl_phase1(struct mac_ax_adapter *adapter, u8 *fw, u32 hdr_len, u8 redl) { u32 ret; u32 cnt = FWDL_WAIT_CNT; struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); if (adapter->sm.fwdl != MAC_AX_FWDL_H2C_PATH_RDY) { PLTFM_MSG_ERR("[ERR]%s: state != H2C_PATH_RDY\n", __func__); return MACPROCERR; } ret = __fwhdr_download(adapter, fw, hdr_len, redl); if (ret) { PLTFM_MSG_ERR("[ERR]%s: __fwhdr_download fail\n", __func__); return ret; } while (--cnt) { if (MAC_REG_R8(R_AX_WCPU_FW_CTRL) & B_AX_FWDL_PATH_RDY) break; PLTFM_DELAY_US(1); } if (!cnt) { PLTFM_MSG_ERR("[ERR]%s: poll 0x1E0[2] = 1 fail\n", __func__); return MACPOLLTO; } MAC_REG_W32(R_AX_HALT_H2C_CTRL, 0); MAC_REG_W32(R_AX_HALT_C2H_CTRL, 0); adapter->sm.fwdl = MAC_AX_FWDL_PATH_RDY; return MACSUCCESS; } static u32 check_fw_rdy(struct mac_ax_adapter *adapter) { u32 val8 = FWDL_INITIAL_STATE; u32 cnt = FWDL_WAIT_CNT; struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); while (--cnt) { val8 = GET_FIELD(MAC_REG_R8(R_AX_WCPU_FW_CTRL), B_AX_WCPU_FWDL_STS); if (val8 == FWDL_WCPU_FW_INIT_RDY) break; PLTFM_DELAY_US(1); } if (!cnt) { PLTFM_MSG_ERR("[ERR]%s: poll 0x1E0[7:5] = 7 fail\n", __func__); switch (val8) { case FWDL_CHECKSUM_FAIL: return MACFWCHKSUM; case FWDL_SECURITY_FAIL: return MACFWSECBOOT; case FWDL_CUT_NOT_MATCH: return MACFWCUT; default: return MACPOLLTO; } } adapter->sm.fwdl = MAC_AX_FWDL_INIT_RDY; return MACSUCCESS; } static u32 fwdl_phase2(struct mac_ax_adapter *adapter, u8 *fw, struct fw_bin_info *info, u8 redl) { u32 ret; u32 section_num = info->section_num; struct fwhdr_section_info *section_info = info->section_info; if (adapter->sm.fwdl != MAC_AX_FWDL_PATH_RDY) { PLTFM_MSG_ERR("[ERR]%s: state != FWDL_PATH_RDY\n", __func__); return MACPROCERR; } while (section_num > 0) { if (!redl) { ret = __sections_download(adapter, section_info); if (ret) { PLTFM_MSG_ERR("[ERR]%s: ", __func__); PLTFM_MSG_ERR("__sections_download fail\n"); return ret; } } else { if (section_info->redl) { ret = __sections_download(adapter, section_info); if (ret) { PLTFM_MSG_ERR("[ERR]%s: ", __func__); PLTFM_MSG_ERR("__sections_download "); PLTFM_MSG_ERR("fail\n"); return ret; } } } section_info++; section_num--; } PLTFM_DELAY_MS(5); ret = check_fw_rdy(adapter); if (ret) { PLTFM_MSG_ERR("%s: check_fw_rdy fail\n", __func__); return ret; } return MACSUCCESS; } static void fwdl_fail_dump(struct mac_ax_adapter *adapter, struct fw_bin_info *info, u32 ret) { u32 val32, digest_addr, digest; u16 index; struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); #if MAC_AX_FEATURE_DBGPKG struct mac_ax_ops *mac_ops = adapter_to_mac_ops(adapter); struct mac_ax_dbgpkg dbg_val = {0}; struct mac_ax_dbgpkg_en dbg_en = {0}; #endif PLTFM_MSG_ERR("[ERR]fwdl ret = %d\n", ret); val32 = MAC_REG_R32(R_AX_WCPU_FW_CTRL); PLTFM_MSG_ERR("[ERR]fwdl 0x1E0 = 0x%x\n", val32); val32 = MAC_REG_R32(R_AX_BOOT_DBG); PLTFM_MSG_ERR("[ERR]fwdl 0x83F0 = 0x%x\n", val32); val32 = MAC_REG_R32(R_AX_UDM3); PLTFM_MSG_ERR("[ERR]fwdl 0x1FC = 0x%x\n", val32); val32 = info->git_idx; PLTFM_MSG_ERR("[ERR]fw git idx = 0x%x\n", val32); PLTFM_MUTEX_LOCK(&adapter->hw_info->dbg_port_lock); adapter->hw_info->dbg_port_cnt++; if (adapter->hw_info->dbg_port_cnt != 1) { PLTFM_MSG_ERR("[ERR]fwdl fail dump lock cnt %d\n", adapter->hw_info->dbg_port_cnt); goto end; } MAC_REG_W32(R_AX_DBG_CTRL, 0xf200f2); val32 = MAC_REG_R32(R_AX_SYS_STATUS1); val32 = SET_CLR_WORD(val32, 0x1, B_AX_SEL_0XC0); MAC_REG_W32(R_AX_SYS_STATUS1, val32); for (index = 0; index < 15; index++) { val32 = MAC_REG_R32(R_AX_DBG_PORT_SEL); PLTFM_MSG_ERR("[ERR]fw PC = 0x%x\n", val32); PLTFM_DELAY_US(10); } PLTFM_MSG_WARN("%s ind access FW digest start\n", __func__); PLTFM_MUTEX_LOCK(&adapter->hw_info->ind_access_lock); adapter->hw_info->ind_aces_cnt++; digest_addr = (0xB8E17C00 + 64) & ~0xA0000000; MAC_REG_W32(R_AX_FILTER_MODEL_ADDR, digest_addr); PLTFM_MSG_ERR("IRAM digest:"); for (index = 0; index < FWDL_DIGEST_SIZE; index += 4) { digest = MAC_REG_R32(R_AX_INDIR_ACCESS_ENTRY + index); PLTFM_MSG_ERR("0x%x\t", digest); } PLTFM_MSG_ERR("DMEM digest:"); for (index = 0; index < FWDL_DIGEST_SIZE; index += 4) { digest = MAC_REG_R32(R_AX_INDIR_ACCESS_ENTRY + FWDL_DIGEST_SIZE + index); PLTFM_MSG_ERR("0x%x\t", digest); } adapter->hw_info->ind_aces_cnt--; PLTFM_MUTEX_UNLOCK(&adapter->hw_info->ind_access_lock); PLTFM_MSG_WARN("%s ind access FW digest end\n", __func__); pltfm_dbg_dump(adapter); mac_ops->dump_ple_dbg_page(adapter, 0); end: #if MAC_AX_FEATURE_DBGPKG dbg_en.ss_dbg = 0; dbg_en.dle_dbg = 1; dbg_en.dmac_dbg = 1; dbg_en.cmac_dbg = 1; dbg_en.mac_dbg_port = 1; dbg_en.plersvd_dbg = 1; mac_ops->dbg_status_dump(adapter, &dbg_val, &dbg_en); #endif adapter->hw_info->dbg_port_cnt--; PLTFM_MUTEX_UNLOCK(&adapter->hw_info->dbg_port_lock); } u32 disable_fw_watchdog(struct mac_ax_adapter *adapter) { u32 val32, ret; ret = mac_sram_dbg_write(adapter, R_AX_WDT_CTRL, B_AX_WDT_CTRL_ALL_DIS, CPU_LOCAL_SEL); if (ret) return ret; val32 = mac_sram_dbg_read(adapter, R_AX_WDT_STATUS, CPU_LOCAL_SEL); val32 = val32 | B_AX_FS_WDT_INT; val32 = val32 & (~B_AX_FS_WDT_INT_MSK); ret = mac_sram_dbg_write(adapter, R_AX_WDT_STATUS, val32, CPU_LOCAL_SEL); if (ret) return ret; return MACSUCCESS; } u32 mac_fwredl(struct mac_ax_adapter *adapter, u8 *fw, u32 len) { u32 val32; u32 ret; struct fw_bin_info info; struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); val32 = MAC_REG_R32(R_AX_WCPU_FW_CTRL); val32 &= ~(B_AX_WCPU_FWDL_EN | B_AX_H2C_PATH_RDY | B_AX_FWDL_PATH_RDY); val32 = SET_CLR_WORD(val32, FWDL_INITIAL_STATE, B_AX_WCPU_FWDL_STS); MAC_REG_W32(R_AX_WCPU_FW_CTRL, val32); ret = fwhdr_parser(adapter, fw, len, &info); if (ret) { PLTFM_MSG_ERR("[ERR]%s: fwhdr_parser fail\n", __func__); goto fwdl_err; } ret = update_fw_ver(adapter, (struct fwhdr_hdr_t *)fw); if (ret) { PLTFM_MSG_ERR("[ERR]%s: update_fw_ver fail\n", __func__); goto fwdl_err; } adapter->sm.fwdl = MAC_AX_FWDL_H2C_PATH_RDY; ret = fwdl_phase1(adapter, fw, info.hdr_len, 1); if (ret) { PLTFM_MSG_ERR("[ERR]%s: fwdl_phase1 fail\n", __func__); goto fwdl_err; } ret = fwdl_phase2(adapter, fw, &info, 1); if (ret) { PLTFM_MSG_ERR("[ERR]%s: fwdl_phase2 fail\n", __func__); goto fwdl_err; } mac_scanofld_reset_state(adapter); return MACSUCCESS; fwdl_err: fwdl_fail_dump(adapter, &info, ret); return ret; } u32 mac_fwdl(struct mac_ax_adapter *adapter, u8 *fw, u32 len) { u8 retry_cnt; u32 ret; struct fw_bin_info info; struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); ret = 0; retry_cnt = 0; MAC_REG_W32(R_AX_UDM1, 0); // FWDL retry, for 025 temp workaround while (retry_cnt < FWDL_TRY_CNT) { if (!fw) { PLTFM_MSG_ERR("[ERR]%s: no fw\n", __func__); ret = MACNOFW; PLTFM_MEMSET(&info, 0, sizeof(struct fw_bin_info)); goto fwdl_err; } ret = fwhdr_parser(adapter, fw, len, &info); if (ret) { PLTFM_MSG_ERR("[ERR]%s: fwhdr_parser fail\n", __func__); goto fwdl_err; } ret = update_fw_ver(adapter, (struct fwhdr_hdr_t *)fw); if (ret) goto fwdl_err; ret = fwdl_phase0(adapter); if (ret) { PLTFM_MSG_ERR("[ERR]%s: fwdl_phase0 fail\n", __func__); goto fwdl_err; } ret = fwdl_phase1(adapter, fw, info.hdr_len, 0); if (ret) { PLTFM_MSG_ERR("[ERR]%s: fwdl_phase1 fail\n", __func__); goto fwdl_err; } ret = fwdl_phase2(adapter, fw, &info, 0); if (ret) { PLTFM_MSG_ERR("[ERR]%s: fwdl_phase2 fail\n", __func__); goto fwdl_err; } return MACSUCCESS; fwdl_err: retry_cnt++; PLTFM_MSG_ERR("[ERR]%s: Retry FWDL count %d\n", __func__, retry_cnt); // At most retry 2 times if (retry_cnt < FWDL_TRY_CNT) { ret = mac_disable_cpu(adapter); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]%s: mac_disable_cpu fail\n", __func__); fwdl_fail_dump(adapter, &info, ret); return ret; } ret = mac_enable_cpu(adapter, AX_BOOT_REASON_PWR_ON, 1); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]%s: mac_enable_cpu fail\n", __func__); fwdl_fail_dump(adapter, &info, ret); return ret; } MAC_REG_W32(R_AX_UDM1, retry_cnt); } else { break; } } fwdl_fail_dump(adapter, &info, ret); return ret; } u32 mac_enable_cpu(struct mac_ax_adapter *adapter, u8 boot_reason, u8 dlfw) { u32 val32, ret; u16 val16; struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); if (MAC_REG_R32(R_AX_PLATFORM_ENABLE) & B_AX_WCPU_EN) return MACCPUSTATE; if (adapter->sm.fwdl != MAC_AX_FWDL_IDLE) { PLTFM_MSG_ERR("[ERR]%s: state != FWDL_IDLE\n", __func__); return MACPROCERR; } //FW cannot support too much log. Reset R_AX_LDM for FW debug config MAC_REG_W32(R_AX_LDM, 0); MAC_REG_W32(R_AX_HALT_H2C_CTRL, 0); MAC_REG_W32(R_AX_HALT_C2H_CTRL, 0); val32 = MAC_REG_R32(R_AX_UDM0); val32 &= ~(B_AX_UDM0_DBG_MODE_CTRL | B_AX_UDM0_TRAP_LOOP_CTRL); MAC_REG_W32(R_AX_UDM0, val32); MAC_REG_W32(R_AX_SYS_CLK_CTRL, MAC_REG_R32(R_AX_SYS_CLK_CTRL) | B_AX_CPU_CLK_EN); val32 = MAC_REG_R32(R_AX_WCPU_FW_CTRL); val32 &= ~(B_AX_WCPU_FWDL_EN | B_AX_H2C_PATH_RDY | B_AX_FWDL_PATH_RDY); val32 = SET_CLR_WORD(val32, FWDL_INITIAL_STATE, B_AX_WCPU_FWDL_STS); if (dlfw) val32 |= B_AX_WCPU_FWDL_EN; MAC_REG_W32(R_AX_WCPU_FW_CTRL, val32); val16 = MAC_REG_R16(R_AX_BOOT_REASON); val16 = SET_CLR_WORD(val16, boot_reason, B_AX_BOOT_REASON); MAC_REG_W16(R_AX_BOOT_REASON, val16); val32 = MAC_REG_R32(R_AX_PLATFORM_ENABLE); MAC_REG_W32(R_AX_PLATFORM_ENABLE, val32 | B_AX_WCPU_EN); adapter->sm.fwdl = MAC_AX_FWDL_CPU_ON; if (!dlfw) { PLTFM_DELAY_MS(5); ret = check_fw_rdy(adapter); if (ret) { PLTFM_MSG_ERR("[ERR]%s: ", __func__); PLTFM_MSG_ERR("check_fw_rdy fail\n"); return ret; } } // Prevent sequence number in HALMAC and FW mismatching reset_lps_seq_num(adapter); return MACSUCCESS; } u32 mac_disable_cpu(struct mac_ax_adapter *adapter) { u32 val32, ret; struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); adapter->sm.fwdl = MAC_AX_FWDL_IDLE; //todo: need to check cpu in safe state before reset CPU val32 = MAC_REG_R32(R_AX_PLATFORM_ENABLE); MAC_REG_W32(R_AX_PLATFORM_ENABLE, val32 & ~B_AX_WCPU_EN); val32 = MAC_REG_R32(R_AX_UDM0); val32 &= ~(B_AX_UDM0_DBG_MODE_CTRL | B_AX_UDM0_TRAP_LOOP_CTRL); MAC_REG_W32(R_AX_UDM0, val32); val32 = MAC_REG_R32(R_AX_WCPU_FW_CTRL); val32 &= ~(B_AX_WCPU_FWDL_EN | B_AX_H2C_PATH_RDY | B_AX_FWDL_PATH_RDY); MAC_REG_W32(R_AX_WCPU_FW_CTRL, val32); val32 = MAC_REG_R32(R_AX_SYS_CLK_CTRL); MAC_REG_W32(R_AX_SYS_CLK_CTRL, val32 & ~B_AX_CPU_CLK_EN); ret = disable_fw_watchdog(adapter); if (ret) { PLTFM_MSG_ERR("[ERR]%s: disable_fw_watchdog fail\n", __func__); return ret; } adapter->sm.plat = MAC_AX_PLAT_OFF; val32 = MAC_REG_R32(R_AX_PLATFORM_ENABLE); MAC_REG_W32(R_AX_PLATFORM_ENABLE, val32 & ~B_AX_PLATFORM_EN); val32 = MAC_REG_R32(R_AX_PLATFORM_ENABLE); MAC_REG_W32(R_AX_PLATFORM_ENABLE, val32 | B_AX_PLATFORM_EN); adapter->sm.plat = MAC_AX_PLAT_ON; return MACSUCCESS; } u32 mac_romdl(struct mac_ax_adapter *adapter, u8 *ROM, u32 ROM_addr, u32 len) { u8 *content = NULL; u32 val32, ret; struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); ret = mac_disable_cpu(adapter); if (ret) return ret; if (!ROM) return MACNOITEM; val32 = MAC_REG_R32(R_AX_SEC_CTRL); if (val32 & BIT(0)) { ret = __write_memory(adapter, ROM, ROM_addr, len); if (ret) return ret; } else { PLTFM_MSG_ERR("[ERR]%s: __write_memory fail\n", __func__); return MACSECUREON; } PLTFM_FREE(content, len); return MACSUCCESS; } u32 mac_ram_boot(struct mac_ax_adapter *adapter, u8 *fw, u32 len) { u32 addr; u32 ret, section_num; struct fw_bin_info info; struct fwhdr_section_info *section_info; struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); ret = mac_disable_cpu(adapter); if (ret) goto fwdl_err; ret = fwhdr_parser(adapter, fw, len, &info); if (ret) { PLTFM_MSG_ERR("[ERR]%s: fwhdr_parser fail\n", __func__); goto fwdl_err; } ret = update_fw_ver(adapter, (struct fwhdr_hdr_t *)fw); if (ret) goto fwdl_err; section_num = info.section_num; section_info = info.section_info; while (section_num > 0) { ret = __write_memory(adapter, section_info->addr, section_info->dladdr, section_info->len); if (ret) goto fwdl_err; section_info++; section_num--; } addr = (0xb8003000 + R_AX_CPU_BOOT_ADDR) & 0x1FFFFFFF; PLTFM_MSG_WARN("%s ind access 0x%X start\n", __func__, addr); PLTFM_MUTEX_LOCK(&adapter->hw_info->ind_access_lock); adapter->hw_info->ind_aces_cnt++; MAC_REG_W32(R_AX_FILTER_MODEL_ADDR, addr); MAC_REG_W32(R_AX_INDIR_ACCESS_ENTRY, (info.section_info[0].dladdr) | 0xA0000000); adapter->hw_info->ind_aces_cnt--; PLTFM_MUTEX_UNLOCK(&adapter->hw_info->ind_access_lock); PLTFM_MSG_WARN("%s ind access 0x%X end\n", __func__, addr); ret = mac_enable_cpu(adapter, AX_BOOT_REASON_PWR_ON, 0); if (ret) { PLTFM_MSG_ERR("[ERR]%s: mac_enable_cpu fail\n", __func__); goto fwdl_err; } PLTFM_DELAY_MS(10); ret = check_fw_rdy(adapter); if (ret) { PLTFM_MSG_ERR("[ERR]%s: check_fw_rdy fail\n", __func__); goto fwdl_err; } return MACSUCCESS; fwdl_err: fwdl_fail_dump(adapter, &info, ret); return ret; } u32 mac_enable_fw(struct mac_ax_adapter *adapter, enum rtw_fw_type cat) { u32 ret = MACSUCCESS; #if defined(PHL_FEATURE_AP) || defined(PHL_FEATURE_NIC) u32 chip_id, cv; u32 fw_len = 0; u8 *fw = NULL; struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); chip_id = GET_FIELD(MAC_REG_R32(R_AX_SYS_CHIPINFO), B_AX_HW_ID); cv = GET_FIELD(MAC_REG_R32(R_AX_SYS_CFG1), B_AX_CHIP_VER); switch (chip_id) { #ifdef CONFIG_RTL8852A case RTL8852A_ID: switch (cv) { #ifdef MAC_FW_8852A_U2 case FWDL_CBV: switch (cat) { #ifdef PHL_FEATURE_AP case RTW_FW_AP: fw_len = array_length_8852a_u2_ap; fw = array_8852a_u2_ap; break; #endif /*PHL_FEATURE_AP*/ #ifdef PHL_FEATURE_NIC case RTW_FW_NIC: fw_len = array_length_8852a_u2_nic; fw = array_8852a_u2_nic; break; #ifdef CONFIG_WOWLAN case RTW_FW_WOWLAN: fw_len = array_length_8852a_u2_wowlan; fw = array_8852a_u2_wowlan; break; #endif /*CONFIG_WOWLAN*/ #endif /*PHL_FEATURE_NIC*/ default: PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__); fw_len = 0; fw = 0; break; } break; #endif /*MAC_FW_8852A_U2*/ #ifdef MAC_FW_8852A_U3 case FWDL_CCV: switch (cat) { #ifdef PHL_FEATURE_AP case RTW_FW_AP: fw_len = array_length_8852a_u3_ap; fw = array_8852a_u3_ap; break; #endif /*PHL_FEATURE_AP*/ #ifdef PHL_FEATURE_NIC case RTW_FW_NIC: fw_len = array_length_8852a_u3_nic; fw = array_8852a_u3_nic; break; #ifdef CONFIG_WOWLAN case RTW_FW_WOWLAN: fw_len = array_length_8852a_u3_wowlan; fw = array_8852a_u3_wowlan; break; #endif /*CONFIG_WOWLAN*/ #endif /*PHL_FEATURE_NIC*/ default: PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__); fw_len = 0; fw = 0; break; } break; #endif /*MAC_FW_8852A_U3*/ default: PLTFM_MSG_ERR("[ERR]%s: invalid cut\n", __func__); fw_len = 0; fw = 0; break; } break; #endif /*CONFIG_RTL8852A*/ #ifdef CONFIG_RTL8852B case RTL8852B_ID: switch (cv) { #ifdef MAC_FW_8852B_U1 case FWDL_CAV: switch (cat) { #ifdef PHL_FEATURE_AP case RTW_FW_AP: fw_len = array_length_8852b_u1_ap; fw = array_8852b_u1_ap; break; #endif /*PHL_FEATURE_AP*/ #ifdef PHL_FEATURE_NIC case RTW_FW_NIC: fw_len = array_length_8852b_u1_nic; fw = array_8852b_u1_nic; break; #ifdef CONFIG_WOWLAN case RTW_FW_WOWLAN: fw_len = array_length_8852b_u1_wowlan; fw = array_8852b_u1_wowlan; break; #endif /*CONFIG_WOWLAN*/ #endif /*PHL_FEATURE_NIC*/ default: PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__); fw_len = 0; fw = 0; break; } break; #endif /*MAC_FW_8852B_U1*/ #ifdef MAC_FW_8852B_U2 case FWDL_CBV: switch (cat) { #ifdef PHL_FEATURE_NIC case RTW_FW_NIC: fw_len = array_length_8852b_u2_nic; fw = array_8852b_u2_nic; break; #ifdef CONFIG_WOWLAN case RTW_FW_WOWLAN: fw_len = array_length_8852b_u2_wowlan; fw = array_8852b_u2_wowlan; break; #endif /*CONFIG_WOWLAN*/ #endif /*PHL_FEATURE_NIC*/ default: PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__); fw_len = 0; fw = 0; break; } break; #endif /*MAC_FW_8852B_U2*/ default: switch (cat) { #ifdef PHL_FEATURE_NIC case RTW_FW_NIC: fw_len = array_length_8852b_u2_nic; fw = array_8852b_u2_nic; break; #ifdef CONFIG_WOWLAN case RTW_FW_WOWLAN: fw_len = array_length_8852b_u2_wowlan; fw = array_8852b_u2_wowlan; break; #endif /*CONFIG_WOWLAN*/ #endif /*PHL_FEATURE_NIC*/ default: PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__); fw_len = 0; fw = 0; break; } break; } break; #endif /*CONFIG_RTL8852B*/ #ifdef CONFIG_RTL8852C case RTL8852C_ID: switch (cv) { #ifdef MAC_FW_8852C_U1 case FWDL_CAV: switch (cat) { #ifdef PHL_FEATURE_AP case RTW_FW_AP: fw_len = array_length_8852c_u1_ap; fw = array_8852c_u1_ap; break; #endif /*PHL_FEATURE_AP*/ #ifdef PHL_FEATURE_NIC case RTW_FW_NIC: fw_len = array_length_8852c_u1_nic; fw = array_8852c_u1_nic; break; #ifdef CONFIG_WOWLAN case RTW_FW_WOWLAN: fw_len = array_length_8852c_u1_wowlan; fw = array_8852c_u1_wowlan; break; #endif /*CONFIG_WOWLAN*/ #endif /*PHL_FEATURE_NIC*/ default: PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__); fw_len = 0; fw = 0; break; } break; #endif /*MAC_FW_8852C_U1*/ default: PLTFM_MSG_ERR("[ERR]%s: invalid cut\n", __func__); fw_len = 0; fw = 0; break; } break; #endif /*CONFIG_RTL8852C*/ default: PLTFM_MSG_ERR("[ERR]%s: invalid chip\n", __func__); fw_len = 0; fw = 0; break; } ret = mac_disable_cpu(adapter); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]%s: mac_disable_cpu fail\n", __func__); return ret; } if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852B) || is_chip_id(adapter, MAC_AX_CHIP_ID_8852C)) { ret = mac_check_OTP(adapter, 1); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]%s: mac_check_OTP fail\n", __func__); return ret; } } ret = mac_enable_cpu(adapter, AX_BOOT_REASON_PWR_ON, 1); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]%s: mac_enable_cpu fail\n", __func__); return ret; } ret = mac_fwdl(adapter, fw, fw_len); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]%s: mac_enable_cpu fail\n", __func__); return ret; } if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852B) || is_chip_id(adapter, MAC_AX_CHIP_ID_8852C)) { ret = mac_check_OTP(adapter, 0); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]%s: mac_check_OTP fail\n", __func__); return ret; } } #endif /* #if defined(PHL_FEATURE_AP) || defined(PHL_FEATURE_NIC) */ return ret; } u32 mac_query_fw_buff(struct mac_ax_adapter *adapter, enum rtw_fw_type cat, u8 **fw, u32 *fw_len) { u32 ret = MACSUCCESS; #if defined(PHL_FEATURE_AP) || defined(PHL_FEATURE_NIC) u32 chip_id, cv; struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); chip_id = GET_FIELD(MAC_REG_R32(R_AX_SYS_CHIPINFO), B_AX_HW_ID); cv = GET_FIELD(MAC_REG_R32(R_AX_SYS_CFG1), B_AX_CHIP_VER); switch (chip_id) { #ifdef CONFIG_RTL8852A case RTL8852A_ID: switch (cv) { #ifdef MAC_FW_8852A_U2 case FWDL_CBV: switch (cat) { #ifdef PHL_FEATURE_AP case RTW_FW_AP: *fw_len = array_length_8852a_u2_ap; *fw = array_8852a_u2_ap; break; #endif /*PHL_FEATURE_AP*/ #ifdef PHL_FEATURE_NIC case RTW_FW_NIC: *fw_len = array_length_8852a_u2_nic; *fw = array_8852a_u2_nic; break; #ifdef CONFIG_WOWLAN case RTW_FW_WOWLAN: *fw_len = array_length_8852a_u2_wowlan; *fw = array_8852a_u2_wowlan; break; #endif /*CONFIG_WOWLAN*/ #endif /*PHL_FEATURE_NIC*/ default: PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__); *fw_len = 0; fw = 0; break; } break; #endif /*MAC_FW_8852A_U2*/ #ifdef MAC_FW_8852A_U3 case FWDL_CCV: switch (cat) { #ifdef PHL_FEATURE_AP case RTW_FW_AP: *fw_len = array_length_8852a_u3_ap; *fw = array_8852a_u3_ap; break; #endif /*PHL_FEATURE_AP*/ #ifdef PHL_FEATURE_NIC case RTW_FW_NIC: *fw_len = array_length_8852a_u3_nic; *fw = array_8852a_u3_nic; break; #ifdef CONFIG_WOWLAN case RTW_FW_WOWLAN: *fw_len = array_length_8852a_u3_wowlan; *fw = array_8852a_u3_wowlan; break; #endif /*CONFIG_WOWLAN*/ #endif /*PHL_FEATURE_NIC*/ default: PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__); *fw_len = 0; fw = 0; break; } break; #endif /*MAC_FW_8852A_U3*/ default: PLTFM_MSG_ERR("[ERR]%s: invalid cut\n", __func__); *fw_len = 0; fw = 0; break; } break; #endif /*MAC_AX_8852A_SUPPORT*/ #ifdef CONFIG_RTL8852B case RTL8852B_ID: switch (cv) { #ifdef MAC_FW_8852B_U2 case FWDL_CBV: switch (cat) { #ifdef PHL_FEATURE_AP case RTW_FW_AP: PLTFM_MSG_ERR("[ERR]%s: 8852b does not have ap image\n", __func__); *fw_len = 0; fw = 0; break; #endif /*PHL_FEATURE_AP*/ #ifdef PHL_FEATURE_NIC case RTW_FW_NIC: *fw_len = array_length_8852b_u2_nic; *fw = array_8852b_u2_nic; break; #ifdef CONFIG_WOWLAN case RTW_FW_WOWLAN: *fw_len = array_length_8852b_u2_wowlan; *fw = array_8852b_u2_wowlan; break; #endif /*CONFIG_WOWLAN*/ #endif /*PHL_FEATURE_NIC*/ default: PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__); *fw_len = 0; fw = 0; break; } break; #endif /*MAC_FW_8852B_U1*/ default: PLTFM_MSG_ERR("[ERR]%s: invalid cut\n", __func__); *fw_len = 0; fw = 0; break; } break; #endif /*MAC_AX_8852B_SUPPORT*/ #ifdef CONFIG_RTL8852C case RTL8852C_ID: switch (cv) { #ifdef MAC_FW_8852C_U1 case FWDL_CAV: switch (cat) { #ifdef PHL_FEATURE_AP case RTW_FW_AP: *fw_len = array_length_8852c_u1_ap; *fw = array_8852c_u1_ap; break; #endif /*PHL_FEATURE_AP*/ #ifdef PHL_FEATURE_NIC case RTW_FW_NIC: *fw_len = array_length_8852c_u1_nic; *fw = array_8852c_u1_nic; break; #ifdef CONFIG_WOWLAN case RTW_FW_WOWLAN: *fw_len = array_length_8852c_u1_wowlan; *fw = array_8852c_u1_wowlan; break; #endif /*CONFIG_WOWLAN*/ #endif /*PHL_FEATURE_NIC*/ default: PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__); *fw_len = 0; fw = 0; break; } break; #endif /*MAC_FW_8852C_U1*/ default: PLTFM_MSG_ERR("[ERR]%s: invalid cut\n", __func__); *fw_len = 0; fw = 0; break; } break; #endif /*MAC_AX_8852C_SUPPORT*/ default: PLTFM_MSG_ERR("[ERR]%s: invalid chip\n", __func__); *fw_len = 0; fw = 0; break; } #endif /* #if defined(PHL_FEATURE_AP) || defined(PHL_FEATURE_NIC) */ return ret; }