Merge tag 'qcom-clk-for-6.4' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom...
[linux-block.git] / drivers / staging / r8188eu / core / rtw_fw.c
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
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
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                 if (rtw_writeN(padapter, addr, block_size, data))
93                         return _FAIL;
94         }
95
96         if (remain) {
97                 offset = blocks * block_size;
98                 block_size = 8;
99
100                 blocks = remain / block_size;
101                 remain = remain % block_size;
102
103                 for (i = 0; i < blocks; i++) {
104                         addr = FW_8188E_START_ADDRESS + offset + i * block_size;
105                         data = buffer + offset + i * block_size;
106
107                         if (rtw_writeN(padapter, addr, block_size, data))
108                                 return _FAIL;
109                 }
110         }
111
112         if (remain) {
113                 offset += blocks * block_size;
114
115                 /* block size 1 */
116                 blocks = remain;
117
118                 for (i = 0; i < blocks; i++) {
119                         addr = FW_8188E_START_ADDRESS + offset + i;
120                         data = buffer + offset + i;
121
122                         ret = rtw_write8(padapter, addr, *data);
123                         if (ret == _FAIL)
124                                 goto exit;
125                 }
126         }
127
128 exit:
129         return ret;
130 }
131
132 static int page_write(struct adapter *padapter, u32 page, u8 *buffer, u32 size)
133 {
134         u8 value8;
135         u8 u8Page = (u8)(page & 0x07);
136         int res;
137
138         res = rtw_read8(padapter, REG_MCUFWDL + 2, &value8);
139         if (res)
140                 return _FAIL;
141
142         value8 = (value8 & 0xF8) | u8Page;
143         rtw_write8(padapter, REG_MCUFWDL + 2, value8);
144
145         return block_write(padapter, buffer, size);
146 }
147
148 static int write_fw(struct adapter *padapter, u8 *buffer, u32 size)
149 {
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. */
152         int ret = _SUCCESS;
153         u32     pageNums, remainSize;
154         u32     page, offset;
155
156         pageNums = size / MAX_PAGE_SIZE;
157         remainSize = size % MAX_PAGE_SIZE;
158
159         for (page = 0; page < pageNums; page++) {
160                 offset = page * MAX_PAGE_SIZE;
161                 ret = page_write(padapter, page, buffer + offset, MAX_PAGE_SIZE);
162
163                 if (ret == _FAIL)
164                         goto exit;
165         }
166         if (remainSize) {
167                 offset = pageNums * MAX_PAGE_SIZE;
168                 page = pageNums;
169                 ret = page_write(padapter, page, buffer + offset, remainSize);
170
171                 if (ret == _FAIL)
172                         goto exit;
173         }
174 exit:
175         return ret;
176 }
177
178 void rtw_reset_8051(struct adapter *padapter)
179 {
180         u8 val8;
181         int res;
182
183         res = rtw_read8(padapter, REG_SYS_FUNC_EN + 1, &val8);
184         if (res)
185                 return;
186
187         rtw_write8(padapter, REG_SYS_FUNC_EN + 1, val8 & (~BIT(2)));
188         rtw_write8(padapter, REG_SYS_FUNC_EN + 1, val8 | (BIT(2)));
189 }
190
191 static int fw_free_to_go(struct adapter *padapter)
192 {
193         u32     counter = 0;
194         u32     value32;
195         int res;
196
197         /*  polling CheckSum report */
198         do {
199                 res = rtw_read32(padapter, REG_MCUFWDL, &value32);
200                 if (res)
201                         continue;
202
203                 if (value32 & FWDL_CHKSUM_RPT)
204                         break;
205         } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
206
207         if (counter >= POLLING_READY_TIMEOUT_COUNT)
208                 return _FAIL;
209
210         res = rtw_read32(padapter, REG_MCUFWDL, &value32);
211         if (res)
212                 return _FAIL;
213
214         value32 |= MCUFWDL_RDY;
215         value32 &= ~WINTINI_RDY;
216         rtw_write32(padapter, REG_MCUFWDL, value32);
217
218         rtw_reset_8051(padapter);
219
220         /*  polling for FW ready */
221         counter = 0;
222         do {
223                 res = rtw_read32(padapter, REG_MCUFWDL, &value32);
224                 if (!res && value32 & WINTINI_RDY)
225                         return _SUCCESS;
226
227                 udelay(5);
228         } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
229
230         return _FAIL;
231 }
232
233 static int load_firmware(struct rt_firmware *rtfw, struct device *device)
234 {
235         int ret = _SUCCESS;
236         const struct firmware *fw;
237         const char *fw_name = FW_RTL8188EU;
238         int err = request_firmware(&fw, fw_name, device);
239
240         if (err) {
241                 pr_err("Request firmware failed with error 0x%x\n", err);
242                 ret = _FAIL;
243                 goto exit;
244         }
245         if (!fw) {
246                 pr_err("Firmware %s not available\n", fw_name);
247                 ret = _FAIL;
248                 goto exit;
249         }
250
251         rtfw->data = kmemdup(fw->data, fw->size, GFP_KERNEL);
252         if (!rtfw->data) {
253                 pr_err("Failed to allocate rtfw->data\n");
254                 ret = _FAIL;
255                 goto exit;
256         }
257         rtfw->size = fw->size;
258
259 exit:
260         release_firmware(fw);
261         return ret;
262 }
263
264 int rtl8188e_firmware_download(struct adapter *padapter)
265 {
266         int ret = _SUCCESS;
267         u8 reg;
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;
272         u8 *fw_data;
273         u32 fw_size;
274
275         if (!dvobj->firmware.data)
276                 ret = load_firmware(&dvobj->firmware, device);
277         if (ret == _FAIL) {
278                 dvobj->firmware.data = NULL;
279                 goto exit;
280         }
281         fw_data = dvobj->firmware.data;
282         fw_size = dvobj->firmware.size;
283
284         fwhdr = (struct rt_firmware_hdr *)dvobj->firmware.data;
285
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));
290
291                 fw_data = fw_data + sizeof(struct rt_firmware_hdr);
292                 fw_size = fw_size - sizeof(struct rt_firmware_hdr);
293         }
294
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, &reg);
298         if (ret) {
299                 ret = _FAIL;
300                 goto exit;
301         }
302
303         if (reg & RAM_DL_SEL) { /* 8051 RAM code */
304                 rtw_write8(padapter, REG_MCUFWDL, 0x00);
305                 rtw_reset_8051(padapter);
306         }
307
308         fw_download_enable(padapter, true);
309         fwdl_timeout = jiffies + msecs_to_jiffies(500);
310         do {
311                 /* reset the FWDL chksum */
312                 ret = rtw_read8(padapter, REG_MCUFWDL, &reg);
313                 if (ret) {
314                         ret = _FAIL;
315                         continue;
316                 }
317
318                 rtw_write8(padapter, REG_MCUFWDL, reg | FWDL_CHKSUM_RPT);
319
320                 ret = write_fw(padapter, fw_data, fw_size);
321                 if (ret == _SUCCESS)
322                         break;
323         } while (!time_after(jiffies, fwdl_timeout));
324
325         fw_download_enable(padapter, false);
326         if (ret != _SUCCESS)
327                 goto exit;
328
329         ret = fw_free_to_go(padapter);
330         if (ret != _SUCCESS)
331                 goto exit;
332
333 exit:
334         return ret;
335 }