// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2007 - 2011 Realtek Corporation. */ #include #include "../include/rtw_fw.h" #define MAX_REG_BOLCK_SIZE 196 #define FW_8188E_START_ADDRESS 0x1000 #define MAX_PAGE_SIZE 4096 #define IS_FW_HEADER_EXIST(_fwhdr) \ ((le16_to_cpu(_fwhdr->Signature) & 0xFFF0) == 0x92C0 || \ (le16_to_cpu(_fwhdr->Signature) & 0xFFF0) == 0x88C0 || \ (le16_to_cpu(_fwhdr->Signature) & 0xFFF0) == 0x2300 || \ (le16_to_cpu(_fwhdr->Signature) & 0xFFF0) == 0x88E0) /* This structure must be careful with byte-ordering */ struct rt_firmware_hdr { /* 8-byte alinment required */ /* LONG WORD 0 ---- */ __le16 Signature; /* 92C0: test chip; 92C, * 88C0: test chip; 88C1: MP A-cut; * 92C1: MP A-cut */ u8 Category; /* AP/NIC and USB/PCI */ u8 Function; /* Reserved for different FW function * indcation, for further use when * driver needs to download different * FW for different conditions */ __le16 Version; /* FW Version */ u8 Subversion; /* FW Subversion, default 0x00 */ u16 Rsvd1; /* LONG WORD 1 ---- */ u8 Month; /* Release time Month field */ u8 Date; /* Release time Date field */ u8 Hour; /* Release time Hour field */ u8 Minute; /* Release time Minute field */ __le16 RamCodeSize; /* The size of RAM code */ u8 Foundry; u8 Rsvd2; /* LONG WORD 2 ---- */ __le32 SvnIdx; /* The SVN entry index */ u32 Rsvd3; /* LONG WORD 3 ---- */ u32 Rsvd4; u32 Rsvd5; }; static void fw_download_enable(struct adapter *padapter, bool enable) { u8 tmp; if (enable) { /* MCU firmware download enable. */ tmp = rtw_read8(padapter, REG_MCUFWDL); rtw_write8(padapter, REG_MCUFWDL, tmp | 0x01); /* 8051 reset */ tmp = rtw_read8(padapter, REG_MCUFWDL + 2); rtw_write8(padapter, REG_MCUFWDL + 2, tmp & 0xf7); } else { /* MCU firmware download disable. */ tmp = rtw_read8(padapter, REG_MCUFWDL); rtw_write8(padapter, REG_MCUFWDL, tmp & 0xfe); /* Reserved for fw extension. */ rtw_write8(padapter, REG_MCUFWDL + 1, 0x00); } } static int block_write(struct adapter *padapter, void *buffer, u32 buffSize) { int ret = _SUCCESS; u32 blockSize_p1 = 4; /* (Default) Phase #1 : PCI muse use 4-byte write to download FW */ u32 blockSize_p2 = 8; /* Phase #2 : Use 8-byte, if Phase#1 use big size to write FW. */ u32 blockSize_p3 = 1; /* Phase #3 : Use 1-byte, the remnant of FW image. */ u32 blockCount_p1 = 0, blockCount_p2 = 0, blockCount_p3 = 0; u32 remainSize_p1 = 0, remainSize_p2 = 0; u8 *bufferPtr = (u8 *)buffer; u32 i = 0, offset = 0; blockSize_p1 = MAX_REG_BOLCK_SIZE; /* 3 Phase #1 */ blockCount_p1 = buffSize / blockSize_p1; remainSize_p1 = buffSize % blockSize_p1; for (i = 0; i < blockCount_p1; i++) { ret = rtw_writeN(padapter, (FW_8188E_START_ADDRESS + i * blockSize_p1), blockSize_p1, (bufferPtr + i * blockSize_p1)); if (ret == _FAIL) goto exit; } /* 3 Phase #2 */ if (remainSize_p1) { offset = blockCount_p1 * blockSize_p1; blockCount_p2 = remainSize_p1 / blockSize_p2; remainSize_p2 = remainSize_p1 % blockSize_p2; for (i = 0; i < blockCount_p2; i++) { ret = rtw_writeN(padapter, (FW_8188E_START_ADDRESS + offset + i * blockSize_p2), blockSize_p2, (bufferPtr + offset + i * blockSize_p2)); if (ret == _FAIL) goto exit; } } /* 3 Phase #3 */ if (remainSize_p2) { offset = (blockCount_p1 * blockSize_p1) + (blockCount_p2 * blockSize_p2); blockCount_p3 = remainSize_p2 / blockSize_p3; for (i = 0; i < blockCount_p3; i++) { ret = rtw_write8(padapter, (FW_8188E_START_ADDRESS + offset + i), *(bufferPtr + offset + i)); if (ret == _FAIL) goto exit; } } exit: return ret; } static int page_write(struct adapter *padapter, u32 page, void *buffer, u32 size) { u8 value8; u8 u8Page = (u8)(page & 0x07); value8 = (rtw_read8(padapter, REG_MCUFWDL + 2) & 0xF8) | u8Page; rtw_write8(padapter, REG_MCUFWDL + 2, value8); return block_write(padapter, buffer, size); } static int write_fw(struct adapter *padapter, void *buffer, u32 size) { /* Since we need dynamic decide method of dwonload fw, so we call this function to get chip version. */ /* We can remove _ReadChipVersion from ReadpadapterInfo8192C later. */ int ret = _SUCCESS; u32 pageNums, remainSize; u32 page, offset; u8 *bufferPtr = (u8 *)buffer; pageNums = size / MAX_PAGE_SIZE; remainSize = size % MAX_PAGE_SIZE; for (page = 0; page < pageNums; page++) { offset = page * MAX_PAGE_SIZE; ret = page_write(padapter, page, bufferPtr + offset, MAX_PAGE_SIZE); if (ret == _FAIL) goto exit; } if (remainSize) { offset = pageNums * MAX_PAGE_SIZE; page = pageNums; ret = page_write(padapter, page, bufferPtr + offset, remainSize); if (ret == _FAIL) goto exit; } exit: return ret; } void rtw_reset_8051(struct adapter *padapter) { u8 val8; val8 = rtw_read8(padapter, REG_SYS_FUNC_EN + 1); rtw_write8(padapter, REG_SYS_FUNC_EN + 1, val8 & (~BIT(2))); rtw_write8(padapter, REG_SYS_FUNC_EN + 1, val8 | (BIT(2))); } static int fw_free_to_go(struct adapter *padapter) { u32 counter = 0; u32 value32; /* polling CheckSum report */ do { value32 = rtw_read32(padapter, REG_MCUFWDL); if (value32 & FWDL_CHKSUM_RPT) break; } while (counter++ < POLLING_READY_TIMEOUT_COUNT); if (counter >= POLLING_READY_TIMEOUT_COUNT) return _FAIL; value32 = rtw_read32(padapter, REG_MCUFWDL); value32 |= MCUFWDL_RDY; value32 &= ~WINTINI_RDY; rtw_write32(padapter, REG_MCUFWDL, value32); rtw_reset_8051(padapter); /* polling for FW ready */ counter = 0; do { value32 = rtw_read32(padapter, REG_MCUFWDL); if (value32 & WINTINI_RDY) return _SUCCESS; udelay(5); } while (counter++ < POLLING_READY_TIMEOUT_COUNT); return _FAIL; } static int load_firmware(struct rt_firmware *rtfw, struct device *device) { int ret = _SUCCESS; const struct firmware *fw; const char *fw_name = "rtlwifi/rtl8188eufw.bin"; int err = request_firmware(&fw, fw_name, device); if (err) { pr_err("Request firmware failed with error 0x%x\n", err); ret = _FAIL; goto exit; } if (!fw) { pr_err("Firmware %s not available\n", fw_name); ret = _FAIL; goto exit; } rtfw->data = kmemdup(fw->data, fw->size, GFP_KERNEL); if (!rtfw->data) { pr_err("Failed to allocate rtfw->data\n"); ret = _FAIL; goto exit; } rtfw->size = fw->size; exit: release_firmware(fw); return ret; } int rtl8188e_firmware_download(struct adapter *padapter) { int ret = _SUCCESS; u8 write_fw_retry = 0; u32 fwdl_start_time; struct dvobj_priv *dvobj = adapter_to_dvobj(padapter); struct device *device = dvobj_to_dev(dvobj); struct rt_firmware_hdr *fwhdr = NULL; u16 fw_version, fw_subversion, fw_signature; u8 *fw_data; u32 fw_size; static int log_version; if (!dvobj->firmware.data) ret = load_firmware(&dvobj->firmware, device); if (ret == _FAIL) { dvobj->firmware.data = NULL; goto exit; } fw_data = dvobj->firmware.data; fw_size = dvobj->firmware.size; /* To Check Fw header. Added by tynli. 2009.12.04. */ fwhdr = (struct rt_firmware_hdr *)dvobj->firmware.data; fw_version = le16_to_cpu(fwhdr->Version); fw_subversion = fwhdr->Subversion; fw_signature = le16_to_cpu(fwhdr->Signature); if (!log_version++) pr_info("%sFirmware Version %d, SubVersion %d, Signature 0x%x\n", DRIVER_PREFIX, fw_version, fw_subversion, fw_signature); if (IS_FW_HEADER_EXIST(fwhdr)) { /* Shift 32 bytes for FW header */ fw_data = fw_data + 32; fw_size = fw_size - 32; } /* Suggested by Filen. If 8051 is running in RAM code, driver should inform Fw to reset by itself, */ /* or it will cause download Fw fail. 2010.02.01. by tynli. */ if (rtw_read8(padapter, REG_MCUFWDL) & RAM_DL_SEL) { /* 8051 RAM code */ rtw_write8(padapter, REG_MCUFWDL, 0x00); rtw_reset_8051(padapter); } fw_download_enable(padapter, true); fwdl_start_time = jiffies; while (1) { /* reset the FWDL chksum */ rtw_write8(padapter, REG_MCUFWDL, rtw_read8(padapter, REG_MCUFWDL) | FWDL_CHKSUM_RPT); ret = write_fw(padapter, fw_data, fw_size); if (ret == _SUCCESS || (rtw_get_passing_time_ms(fwdl_start_time) > 500 && write_fw_retry++ >= 3)) break; } fw_download_enable(padapter, false); if (ret != _SUCCESS) goto exit; ret = fw_free_to_go(padapter); if (ret != _SUCCESS) goto exit; exit: return ret; }