1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright(c) 2007 - 2011 Realtek Corporation. */
3
4 #include <linux/firmware.h>
5 #include "../include/rtw_fw.h"
6
7 #define MAX_REG_BLOCK_SIZE 196
8 #define FW_8188E_START_ADDRESS 0x1000
9 #define MAX_PAGE_SIZE 4096
10
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)
16
17 struct rt_firmware_hdr {
18 __le16 signature; /* 92C0: test chip; 92C,
19 * 88C0: test chip; 88C1: MP A-cut;
20 * 92C1: 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 */
28 u8 rsvd1;
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 */
34 u8 foundry;
35 u8 rsvd2;
36 __le32 svnidx; /* The SVN entry index */
37 __le32 rsvd3;
38 __le32 rsvd4;
39 __le32 rsvd5;
40 };
41
42 static_assert(sizeof(struct rt_firmware_hdr) == 32);
43
fw_download_enable(struct adapter * padapter,bool enable)44 static void fw_download_enable(struct adapter *padapter, bool enable)
45 {
46 u8 tmp;
47 int res;
48
49 if (enable) {
50 /* MCU firmware download enable. */
51 res = rtw_read8(padapter, REG_MCUFWDL, &tmp);
52 if (res)
53 return;
54
55 rtw_write8(padapter, REG_MCUFWDL, tmp | 0x01);
56
57 /* 8051 reset */
58 res = rtw_read8(padapter, REG_MCUFWDL + 2, &tmp);
59 if (res)
60 return;
61
62 rtw_write8(padapter, REG_MCUFWDL + 2, tmp & 0xf7);
63 } else {
64 /* MCU firmware download disable. */
65 res = rtw_read8(padapter, REG_MCUFWDL, &tmp);
66 if (res)
67 return;
68
69 rtw_write8(padapter, REG_MCUFWDL, tmp & 0xfe);
70
71 /* Reserved for fw extension. */
72 rtw_write8(padapter, REG_MCUFWDL + 1, 0x00);
73 }
74 }
75
block_write(struct adapter * padapter,u8 * buffer,u32 size)76 static int block_write(struct adapter *padapter, u8 *buffer, u32 size)
77 {
78 int ret = _SUCCESS;
79 u32 blocks, block_size, remain;
80 u32 i, offset, addr;
81 u8 *data;
82
83 block_size = MAX_REG_BLOCK_SIZE;
84
85 blocks = size / block_size;
86 remain = size % block_size;
87
88 for (i = 0; i < blocks; i++) {
89 addr = FW_8188E_START_ADDRESS + i * block_size;
90 data = buffer + i * block_size;
91
92 ret = rtw_writeN(padapter, addr, block_size, data);
93 if (ret == _FAIL)
94 goto exit;
95 }
96
97 if (remain) {
98 offset = blocks * block_size;
99 block_size = 8;
100
101 blocks = remain / block_size;
102 remain = remain % block_size;
103
104 for (i = 0; i < blocks; i++) {
105 addr = FW_8188E_START_ADDRESS + offset + i * block_size;
106 data = buffer + offset + i * block_size;
107
108 ret = rtw_writeN(padapter, addr, block_size, data);
109 if (ret == _FAIL)
110 goto exit;
111 }
112 }
113
114 if (remain) {
115 offset += blocks * block_size;
116
117 /* block size 1 */
118 blocks = remain;
119
120 for (i = 0; i < blocks; i++) {
121 addr = FW_8188E_START_ADDRESS + offset + i;
122 data = buffer + offset + i;
123
124 ret = rtw_write8(padapter, addr, *data);
125 if (ret == _FAIL)
126 goto exit;
127 }
128 }
129
130 exit:
131 return ret;
132 }
133
page_write(struct adapter * padapter,u32 page,u8 * buffer,u32 size)134 static int page_write(struct adapter *padapter, u32 page, u8 *buffer, u32 size)
135 {
136 u8 value8;
137 u8 u8Page = (u8)(page & 0x07);
138 int res;
139
140 res = rtw_read8(padapter, REG_MCUFWDL + 2, &value8);
141 if (res)
142 return _FAIL;
143
144 value8 = (value8 & 0xF8) | u8Page;
145 rtw_write8(padapter, REG_MCUFWDL + 2, value8);
146
147 return block_write(padapter, buffer, size);
148 }
149
write_fw(struct adapter * padapter,u8 * buffer,u32 size)150 static int write_fw(struct adapter *padapter, u8 *buffer, u32 size)
151 {
152 /* Since we need dynamic decide method of dwonload fw, so we call this function to get chip version. */
153 /* We can remove _ReadChipVersion from ReadpadapterInfo8192C later. */
154 int ret = _SUCCESS;
155 u32 pageNums, remainSize;
156 u32 page, offset;
157
158 pageNums = size / MAX_PAGE_SIZE;
159 remainSize = size % MAX_PAGE_SIZE;
160
161 for (page = 0; page < pageNums; page++) {
162 offset = page * MAX_PAGE_SIZE;
163 ret = page_write(padapter, page, buffer + offset, MAX_PAGE_SIZE);
164
165 if (ret == _FAIL)
166 goto exit;
167 }
168 if (remainSize) {
169 offset = pageNums * MAX_PAGE_SIZE;
170 page = pageNums;
171 ret = page_write(padapter, page, buffer + offset, remainSize);
172
173 if (ret == _FAIL)
174 goto exit;
175 }
176 exit:
177 return ret;
178 }
179
rtw_reset_8051(struct adapter * padapter)180 void rtw_reset_8051(struct adapter *padapter)
181 {
182 u8 val8;
183 int res;
184
185 res = rtw_read8(padapter, REG_SYS_FUNC_EN + 1, &val8);
186 if (res)
187 return;
188
189 rtw_write8(padapter, REG_SYS_FUNC_EN + 1, val8 & (~BIT(2)));
190 rtw_write8(padapter, REG_SYS_FUNC_EN + 1, val8 | (BIT(2)));
191 }
192
fw_free_to_go(struct adapter * padapter)193 static int fw_free_to_go(struct adapter *padapter)
194 {
195 u32 counter = 0;
196 u32 value32;
197 int res;
198
199 /* polling CheckSum report */
200 do {
201 res = rtw_read32(padapter, REG_MCUFWDL, &value32);
202 if (res)
203 continue;
204
205 if (value32 & FWDL_CHKSUM_RPT)
206 break;
207 } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
208
209 if (counter >= POLLING_READY_TIMEOUT_COUNT)
210 return _FAIL;
211
212 res = rtw_read32(padapter, REG_MCUFWDL, &value32);
213 if (res)
214 return _FAIL;
215
216 value32 |= MCUFWDL_RDY;
217 value32 &= ~WINTINI_RDY;
218 rtw_write32(padapter, REG_MCUFWDL, value32);
219
220 rtw_reset_8051(padapter);
221
222 /* polling for FW ready */
223 counter = 0;
224 do {
225 res = rtw_read32(padapter, REG_MCUFWDL, &value32);
226 if (!res && value32 & WINTINI_RDY)
227 return _SUCCESS;
228
229 udelay(5);
230 } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
231
232 return _FAIL;
233 }
234
load_firmware(struct rt_firmware * rtfw,struct device * device)235 static int load_firmware(struct rt_firmware *rtfw, struct device *device)
236 {
237 int ret = _SUCCESS;
238 const struct firmware *fw;
239 const char *fw_name = "rtlwifi/rtl8188eufw.bin";
240 int err = request_firmware(&fw, fw_name, device);
241
242 if (err) {
243 pr_err("Request firmware failed with error 0x%x\n", err);
244 ret = _FAIL;
245 goto exit;
246 }
247 if (!fw) {
248 pr_err("Firmware %s not available\n", fw_name);
249 ret = _FAIL;
250 goto exit;
251 }
252
253 rtfw->data = kmemdup(fw->data, fw->size, GFP_KERNEL);
254 if (!rtfw->data) {
255 pr_err("Failed to allocate rtfw->data\n");
256 ret = _FAIL;
257 goto exit;
258 }
259 rtfw->size = fw->size;
260
261 exit:
262 release_firmware(fw);
263 return ret;
264 }
265
rtl8188e_firmware_download(struct adapter * padapter)266 int rtl8188e_firmware_download(struct adapter *padapter)
267 {
268 int ret = _SUCCESS;
269 u8 reg;
270 unsigned long fwdl_timeout;
271 struct dvobj_priv *dvobj = adapter_to_dvobj(padapter);
272 struct device *device = dvobj_to_dev(dvobj);
273 struct rt_firmware_hdr *fwhdr = NULL;
274 u8 *fw_data;
275 u32 fw_size;
276
277 if (!dvobj->firmware.data)
278 ret = load_firmware(&dvobj->firmware, device);
279 if (ret == _FAIL) {
280 dvobj->firmware.data = NULL;
281 goto exit;
282 }
283 fw_data = dvobj->firmware.data;
284 fw_size = dvobj->firmware.size;
285
286 fwhdr = (struct rt_firmware_hdr *)dvobj->firmware.data;
287
288 if (IS_FW_HEADER_EXIST(fwhdr)) {
289 pr_info_once("R8188EU: Firmware Version %d, SubVersion %d, Signature 0x%x\n",
290 le16_to_cpu(fwhdr->version), fwhdr->subversion,
291 le16_to_cpu(fwhdr->signature));
292
293 fw_data = fw_data + sizeof(struct rt_firmware_hdr);
294 fw_size = fw_size - sizeof(struct rt_firmware_hdr);
295 }
296
297 /* Suggested by Filen. If 8051 is running in RAM code, driver should inform Fw to reset by itself, */
298 /* or it will cause download Fw fail. 2010.02.01. by tynli. */
299 ret = rtw_read8(padapter, REG_MCUFWDL, ®);
300 if (ret) {
301 ret = _FAIL;
302 goto exit;
303 }
304
305 if (reg & RAM_DL_SEL) { /* 8051 RAM code */
306 rtw_write8(padapter, REG_MCUFWDL, 0x00);
307 rtw_reset_8051(padapter);
308 }
309
310 fw_download_enable(padapter, true);
311 fwdl_timeout = jiffies + msecs_to_jiffies(500);
312 do {
313 /* reset the FWDL chksum */
314 ret = rtw_read8(padapter, REG_MCUFWDL, ®);
315 if (ret) {
316 ret = _FAIL;
317 continue;
318 }
319
320 rtw_write8(padapter, REG_MCUFWDL, reg | FWDL_CHKSUM_RPT);
321
322 ret = write_fw(padapter, fw_data, fw_size);
323 if (ret == _SUCCESS)
324 break;
325 } while (!time_after(jiffies, fwdl_timeout));
326
327 fw_download_enable(padapter, false);
328 if (ret != _SUCCESS)
329 goto exit;
330
331 ret = fw_free_to_go(padapter);
332 if (ret != _SUCCESS)
333 goto exit;
334
335 exit:
336 return ret;
337 }
338