1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright(c) 2007 - 2011 Realtek Corporation. */
4 #include <linux/firmware.h>
5 #include "../include/rtw_fw.h"
7 #define MAX_REG_BLOCK_SIZE 196
8 #define FW_8188E_START_ADDRESS 0x1000
9 #define MAX_PAGE_SIZE 4096
11 #define IS_FW_HEADER_EXIST(_fwhdr) \
12 ((le16_to_cpu(_fwhdr->signature) & 0xFFF0) == 0x92C0 || \
13 (le16_to_cpu(_fwhdr->signature) & 0xFFF0) == 0x88C0 || \
14 (le16_to_cpu(_fwhdr->signature) & 0xFFF0) == 0x2300 || \
15 (le16_to_cpu(_fwhdr->signature) & 0xFFF0) == 0x88E0)
17 struct rt_firmware_hdr {
18 __le16 signature; /* 92C0: test chip; 92C,
19 * 88C0: test chip; 88C1: MP A-cut;
21 u8 category; /* AP/NIC and USB/PCI */
22 u8 function; /* Reserved for different FW function
23 * indcation, for further use when
24 * driver needs to download different
25 * FW for different conditions */
26 __le16 version; /* FW Version */
27 u8 subversion; /* FW Subversion, default 0x00 */
29 u8 month; /* Release time Month field */
30 u8 date; /* Release time Date field */
31 u8 hour; /* Release time Hour field */
32 u8 minute; /* Release time Minute field */
33 __le16 ramcodesize; /* The size of RAM code */
36 __le32 svnidx; /* The SVN entry index */
42 static_assert(sizeof(struct rt_firmware_hdr) == 32);
44 static void fw_download_enable(struct adapter *padapter, bool enable)
50 /* MCU firmware download enable. */
51 res = rtw_read8(padapter, REG_MCUFWDL, &tmp);
55 rtw_write8(padapter, REG_MCUFWDL, tmp | 0x01);
58 res = rtw_read8(padapter, REG_MCUFWDL + 2, &tmp);
62 rtw_write8(padapter, REG_MCUFWDL + 2, tmp & 0xf7);
64 /* MCU firmware download disable. */
65 res = rtw_read8(padapter, REG_MCUFWDL, &tmp);
69 rtw_write8(padapter, REG_MCUFWDL, tmp & 0xfe);
71 /* Reserved for fw extension. */
72 rtw_write8(padapter, REG_MCUFWDL + 1, 0x00);
76 static int block_write(struct adapter *padapter, u8 *buffer, u32 size)
79 u32 blocks, block_size, remain;
83 block_size = MAX_REG_BLOCK_SIZE;
85 blocks = size / block_size;
86 remain = size % block_size;
88 for (i = 0; i < blocks; i++) {
89 addr = FW_8188E_START_ADDRESS + i * block_size;
90 data = buffer + i * block_size;
92 if (rtw_writeN(padapter, addr, block_size, data))
97 offset = blocks * block_size;
100 blocks = remain / block_size;
101 remain = remain % block_size;
103 for (i = 0; i < blocks; i++) {
104 addr = FW_8188E_START_ADDRESS + offset + i * block_size;
105 data = buffer + offset + i * block_size;
107 if (rtw_writeN(padapter, addr, block_size, data))
113 offset += blocks * block_size;
118 for (i = 0; i < blocks; i++) {
119 addr = FW_8188E_START_ADDRESS + offset + i;
120 data = buffer + offset + i;
122 ret = rtw_write8(padapter, addr, *data);
132 static int page_write(struct adapter *padapter, u32 page, u8 *buffer, u32 size)
135 u8 u8Page = (u8)(page & 0x07);
138 res = rtw_read8(padapter, REG_MCUFWDL + 2, &value8);
142 value8 = (value8 & 0xF8) | u8Page;
143 rtw_write8(padapter, REG_MCUFWDL + 2, value8);
145 return block_write(padapter, buffer, size);
148 static int write_fw(struct adapter *padapter, u8 *buffer, u32 size)
150 /* Since we need dynamic decide method of dwonload fw, so we call this function to get chip version. */
151 /* We can remove _ReadChipVersion from ReadpadapterInfo8192C later. */
153 u32 pageNums, remainSize;
156 pageNums = size / MAX_PAGE_SIZE;
157 remainSize = size % MAX_PAGE_SIZE;
159 for (page = 0; page < pageNums; page++) {
160 offset = page * MAX_PAGE_SIZE;
161 ret = page_write(padapter, page, buffer + offset, MAX_PAGE_SIZE);
167 offset = pageNums * MAX_PAGE_SIZE;
169 ret = page_write(padapter, page, buffer + offset, remainSize);
178 void rtw_reset_8051(struct adapter *padapter)
183 res = rtw_read8(padapter, REG_SYS_FUNC_EN + 1, &val8);
187 rtw_write8(padapter, REG_SYS_FUNC_EN + 1, val8 & (~BIT(2)));
188 rtw_write8(padapter, REG_SYS_FUNC_EN + 1, val8 | (BIT(2)));
191 static int fw_free_to_go(struct adapter *padapter)
197 /* polling CheckSum report */
199 res = rtw_read32(padapter, REG_MCUFWDL, &value32);
203 if (value32 & FWDL_CHKSUM_RPT)
205 } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
207 if (counter >= POLLING_READY_TIMEOUT_COUNT)
210 res = rtw_read32(padapter, REG_MCUFWDL, &value32);
214 value32 |= MCUFWDL_RDY;
215 value32 &= ~WINTINI_RDY;
216 rtw_write32(padapter, REG_MCUFWDL, value32);
218 rtw_reset_8051(padapter);
220 /* polling for FW ready */
223 res = rtw_read32(padapter, REG_MCUFWDL, &value32);
224 if (!res && value32 & WINTINI_RDY)
228 } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
233 static int load_firmware(struct rt_firmware *rtfw, struct device *device)
236 const struct firmware *fw;
237 const char *fw_name = FW_RTL8188EU;
238 int err = request_firmware(&fw, fw_name, device);
241 pr_err("Request firmware failed with error 0x%x\n", err);
246 pr_err("Firmware %s not available\n", fw_name);
251 rtfw->data = kmemdup(fw->data, fw->size, GFP_KERNEL);
253 pr_err("Failed to allocate rtfw->data\n");
257 rtfw->size = fw->size;
260 release_firmware(fw);
264 int rtl8188e_firmware_download(struct adapter *padapter)
268 unsigned long fwdl_timeout;
269 struct dvobj_priv *dvobj = adapter_to_dvobj(padapter);
270 struct device *device = dvobj_to_dev(dvobj);
271 struct rt_firmware_hdr *fwhdr = NULL;
275 if (!dvobj->firmware.data)
276 ret = load_firmware(&dvobj->firmware, device);
278 dvobj->firmware.data = NULL;
281 fw_data = dvobj->firmware.data;
282 fw_size = dvobj->firmware.size;
284 fwhdr = (struct rt_firmware_hdr *)dvobj->firmware.data;
286 if (IS_FW_HEADER_EXIST(fwhdr)) {
287 dev_info_once(device, "Firmware Version %d, SubVersion %d, Signature 0x%x\n",
288 le16_to_cpu(fwhdr->version), fwhdr->subversion,
289 le16_to_cpu(fwhdr->signature));
291 fw_data = fw_data + sizeof(struct rt_firmware_hdr);
292 fw_size = fw_size - sizeof(struct rt_firmware_hdr);
295 /* Suggested by Filen. If 8051 is running in RAM code, driver should inform Fw to reset by itself, */
296 /* or it will cause download Fw fail. 2010.02.01. by tynli. */
297 ret = rtw_read8(padapter, REG_MCUFWDL, ®);
303 if (reg & RAM_DL_SEL) { /* 8051 RAM code */
304 rtw_write8(padapter, REG_MCUFWDL, 0x00);
305 rtw_reset_8051(padapter);
308 fw_download_enable(padapter, true);
309 fwdl_timeout = jiffies + msecs_to_jiffies(500);
311 /* reset the FWDL chksum */
312 ret = rtw_read8(padapter, REG_MCUFWDL, ®);
318 rtw_write8(padapter, REG_MCUFWDL, reg | FWDL_CHKSUM_RPT);
320 ret = write_fw(padapter, fw_data, fw_size);
323 } while (!time_after(jiffies, fwdl_timeout));
325 fw_download_enable(padapter, false);
329 ret = fw_free_to_go(padapter);