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 = FW_RTL8188EU;
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 		dev_info_once(device, "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, &reg);
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, &reg);
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