Commit | Line | Data |
---|---|---|
701307a8 CL |
1 | /****************************************************************************** |
2 | * | |
ca742cd9 | 3 | * Copyright(c) 2009-2012 Realtek Corporation. |
701307a8 CL |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | * more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License along with | |
15 | * this program; if not, write to the Free Software Foundation, Inc., | |
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA | |
17 | * | |
18 | * The full GNU General Public License is included in this distribution in the | |
19 | * file called LICENSE. | |
20 | * | |
21 | * Contact Information: | |
22 | * wlanfae <wlanfae@realtek.com> | |
23 | * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, | |
24 | * Hsinchu 300, Taiwan. | |
25 | * | |
26 | * Larry Finger <Larry.Finger@lwfinger.net> | |
27 | * | |
28 | *****************************************************************************/ | |
29 | ||
30 | #include "../wifi.h" | |
31 | #include "../pci.h" | |
32 | #include "../base.h" | |
33 | #include "reg.h" | |
34 | #include "def.h" | |
35 | #include "fw.h" | |
36 | ||
37 | static void _rtl92s_fw_set_rqpn(struct ieee80211_hw *hw) | |
38 | { | |
39 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
40 | ||
41 | rtl_write_dword(rtlpriv, RQPN, 0xffffffff); | |
42 | rtl_write_dword(rtlpriv, RQPN + 4, 0xffffffff); | |
43 | rtl_write_byte(rtlpriv, RQPN + 8, 0xff); | |
44 | rtl_write_byte(rtlpriv, RQPN + 0xB, 0x80); | |
45 | } | |
46 | ||
47 | static bool _rtl92s_firmware_enable_cpu(struct ieee80211_hw *hw) | |
48 | { | |
49 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
50 | u32 ichecktime = 200; | |
51 | u16 tmpu2b; | |
52 | u8 tmpu1b, cpustatus = 0; | |
53 | ||
54 | _rtl92s_fw_set_rqpn(hw); | |
55 | ||
56 | /* Enable CPU. */ | |
57 | tmpu1b = rtl_read_byte(rtlpriv, SYS_CLKR); | |
58 | /* AFE source */ | |
59 | rtl_write_byte(rtlpriv, SYS_CLKR, (tmpu1b | SYS_CPU_CLKSEL)); | |
60 | ||
61 | tmpu2b = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN); | |
62 | rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, (tmpu2b | FEN_CPUEN)); | |
63 | ||
64 | /* Polling IMEM Ready after CPU has refilled. */ | |
65 | do { | |
66 | cpustatus = rtl_read_byte(rtlpriv, TCR); | |
67 | if (cpustatus & IMEM_RDY) { | |
68 | RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, | |
f30d7507 | 69 | "IMEM Ready after CPU has refilled\n"); |
701307a8 CL |
70 | break; |
71 | } | |
72 | ||
73 | udelay(100); | |
74 | } while (ichecktime--); | |
75 | ||
76 | if (!(cpustatus & IMEM_RDY)) | |
77 | return false; | |
78 | ||
79 | return true; | |
80 | } | |
81 | ||
82 | static enum fw_status _rtl92s_firmware_get_nextstatus( | |
83 | enum fw_status fw_currentstatus) | |
84 | { | |
85 | enum fw_status next_fwstatus = 0; | |
86 | ||
87 | switch (fw_currentstatus) { | |
88 | case FW_STATUS_INIT: | |
89 | next_fwstatus = FW_STATUS_LOAD_IMEM; | |
90 | break; | |
91 | case FW_STATUS_LOAD_IMEM: | |
92 | next_fwstatus = FW_STATUS_LOAD_EMEM; | |
93 | break; | |
94 | case FW_STATUS_LOAD_EMEM: | |
95 | next_fwstatus = FW_STATUS_LOAD_DMEM; | |
96 | break; | |
97 | case FW_STATUS_LOAD_DMEM: | |
98 | next_fwstatus = FW_STATUS_READY; | |
99 | break; | |
100 | default: | |
101 | break; | |
102 | } | |
103 | ||
104 | return next_fwstatus; | |
105 | } | |
106 | ||
107 | static u8 _rtl92s_firmware_header_map_rftype(struct ieee80211_hw *hw) | |
108 | { | |
109 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
110 | struct rtl_phy *rtlphy = &(rtlpriv->phy); | |
111 | ||
112 | switch (rtlphy->rf_type) { | |
113 | case RF_1T1R: | |
114 | return 0x11; | |
115 | break; | |
116 | case RF_1T2R: | |
117 | return 0x12; | |
118 | break; | |
119 | case RF_2T2R: | |
120 | return 0x22; | |
121 | break; | |
122 | default: | |
f30d7507 JP |
123 | RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "Unknown RF type(%x)\n", |
124 | rtlphy->rf_type); | |
701307a8 CL |
125 | break; |
126 | } | |
127 | return 0x22; | |
128 | } | |
129 | ||
130 | static void _rtl92s_firmwareheader_priveupdate(struct ieee80211_hw *hw, | |
131 | struct fw_priv *pfw_priv) | |
132 | { | |
133 | /* Update RF types for RATR settings. */ | |
134 | pfw_priv->rf_config = _rtl92s_firmware_header_map_rftype(hw); | |
135 | } | |
136 | ||
137 | ||
138 | ||
139 | static bool _rtl92s_cmd_send_packet(struct ieee80211_hw *hw, | |
140 | struct sk_buff *skb, u8 last) | |
141 | { | |
142 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
143 | struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); | |
144 | struct rtl8192_tx_ring *ring; | |
145 | struct rtl_tx_desc *pdesc; | |
146 | unsigned long flags; | |
147 | u8 idx = 0; | |
148 | ||
149 | ring = &rtlpci->tx_ring[TXCMD_QUEUE]; | |
150 | ||
151 | spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); | |
152 | ||
153 | idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries; | |
154 | pdesc = &ring->desc[idx]; | |
155 | rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *)pdesc, 1, 1, skb); | |
156 | __skb_queue_tail(&ring->queue, skb); | |
157 | ||
158 | spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags); | |
159 | ||
160 | return true; | |
161 | } | |
162 | ||
163 | static bool _rtl92s_firmware_downloadcode(struct ieee80211_hw *hw, | |
164 | u8 *code_virtual_address, u32 buffer_len) | |
165 | { | |
166 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
167 | struct sk_buff *skb; | |
168 | struct rtl_tcb_desc *tcb_desc; | |
169 | unsigned char *seg_ptr; | |
170 | u16 frag_threshold = MAX_FIRMWARE_CODE_SIZE; | |
171 | u16 frag_length, frag_offset = 0; | |
172 | u16 extra_descoffset = 0; | |
173 | u8 last_inipkt = 0; | |
174 | ||
175 | _rtl92s_fw_set_rqpn(hw); | |
176 | ||
177 | if (buffer_len >= MAX_FIRMWARE_CODE_SIZE) { | |
178 | RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, | |
f30d7507 | 179 | "Size over FIRMWARE_CODE_SIZE!\n"); |
701307a8 CL |
180 | |
181 | return false; | |
182 | } | |
183 | ||
184 | extra_descoffset = 0; | |
185 | ||
186 | do { | |
187 | if ((buffer_len - frag_offset) > frag_threshold) { | |
188 | frag_length = frag_threshold + extra_descoffset; | |
189 | } else { | |
190 | frag_length = (u16)(buffer_len - frag_offset + | |
191 | extra_descoffset); | |
192 | last_inipkt = 1; | |
193 | } | |
194 | ||
195 | /* Allocate skb buffer to contain firmware */ | |
196 | /* info and tx descriptor info. */ | |
197 | skb = dev_alloc_skb(frag_length); | |
d90db4b1 LF |
198 | if (!skb) |
199 | return false; | |
701307a8 CL |
200 | skb_reserve(skb, extra_descoffset); |
201 | seg_ptr = (u8 *)skb_put(skb, (u32)(frag_length - | |
202 | extra_descoffset)); | |
203 | memcpy(seg_ptr, code_virtual_address + frag_offset, | |
204 | (u32)(frag_length - extra_descoffset)); | |
205 | ||
206 | tcb_desc = (struct rtl_tcb_desc *)(skb->cb); | |
207 | tcb_desc->queue_index = TXCMD_QUEUE; | |
208 | tcb_desc->cmd_or_init = DESC_PACKET_TYPE_INIT; | |
209 | tcb_desc->last_inipkt = last_inipkt; | |
210 | ||
211 | _rtl92s_cmd_send_packet(hw, skb, last_inipkt); | |
212 | ||
213 | frag_offset += (frag_length - extra_descoffset); | |
214 | ||
215 | } while (frag_offset < buffer_len); | |
216 | ||
217 | rtl_write_byte(rtlpriv, TP_POLL, TPPOLL_CQ); | |
218 | ||
219 | return true ; | |
220 | } | |
221 | ||
222 | static bool _rtl92s_firmware_checkready(struct ieee80211_hw *hw, | |
223 | u8 loadfw_status) | |
224 | { | |
225 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
226 | struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); | |
227 | struct rt_firmware *firmware = (struct rt_firmware *)rtlhal->pfirmware; | |
228 | u32 tmpu4b; | |
229 | u8 cpustatus = 0; | |
230 | short pollingcnt = 1000; | |
231 | bool rtstatus = true; | |
232 | ||
f30d7507 JP |
233 | RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, |
234 | "LoadStaus(%d)\n", loadfw_status); | |
701307a8 CL |
235 | |
236 | firmware->fwstatus = (enum fw_status)loadfw_status; | |
237 | ||
238 | switch (loadfw_status) { | |
239 | case FW_STATUS_LOAD_IMEM: | |
240 | /* Polling IMEM code done. */ | |
241 | do { | |
242 | cpustatus = rtl_read_byte(rtlpriv, TCR); | |
243 | if (cpustatus & IMEM_CODE_DONE) | |
244 | break; | |
245 | udelay(5); | |
246 | } while (pollingcnt--); | |
247 | ||
248 | if (!(cpustatus & IMEM_CHK_RPT) || (pollingcnt <= 0)) { | |
249 | RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, | |
f30d7507 JP |
250 | "FW_STATUS_LOAD_IMEM FAIL CPU, Status=%x\n", |
251 | cpustatus); | |
701307a8 CL |
252 | goto status_check_fail; |
253 | } | |
254 | break; | |
255 | ||
256 | case FW_STATUS_LOAD_EMEM: | |
257 | /* Check Put Code OK and Turn On CPU */ | |
258 | /* Polling EMEM code done. */ | |
259 | do { | |
260 | cpustatus = rtl_read_byte(rtlpriv, TCR); | |
261 | if (cpustatus & EMEM_CODE_DONE) | |
262 | break; | |
263 | udelay(5); | |
264 | } while (pollingcnt--); | |
265 | ||
266 | if (!(cpustatus & EMEM_CHK_RPT) || (pollingcnt <= 0)) { | |
267 | RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, | |
f30d7507 JP |
268 | "FW_STATUS_LOAD_EMEM FAIL CPU, Status=%x\n", |
269 | cpustatus); | |
701307a8 CL |
270 | goto status_check_fail; |
271 | } | |
272 | ||
273 | /* Turn On CPU */ | |
274 | rtstatus = _rtl92s_firmware_enable_cpu(hw); | |
23677ce3 | 275 | if (!rtstatus) { |
701307a8 | 276 | RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, |
f30d7507 | 277 | "Enable CPU fail!\n"); |
701307a8 CL |
278 | goto status_check_fail; |
279 | } | |
280 | break; | |
281 | ||
282 | case FW_STATUS_LOAD_DMEM: | |
283 | /* Polling DMEM code done */ | |
284 | do { | |
285 | cpustatus = rtl_read_byte(rtlpriv, TCR); | |
286 | if (cpustatus & DMEM_CODE_DONE) | |
287 | break; | |
288 | udelay(5); | |
289 | } while (pollingcnt--); | |
290 | ||
291 | if (!(cpustatus & DMEM_CODE_DONE) || (pollingcnt <= 0)) { | |
292 | RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, | |
f30d7507 JP |
293 | "Polling DMEM code done fail ! cpustatus(%#x)\n", |
294 | cpustatus); | |
701307a8 CL |
295 | goto status_check_fail; |
296 | } | |
297 | ||
298 | RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, | |
f30d7507 JP |
299 | "DMEM code download success, cpustatus(%#x)\n", |
300 | cpustatus); | |
701307a8 CL |
301 | |
302 | /* Prevent Delay too much and being scheduled out */ | |
303 | /* Polling Load Firmware ready */ | |
304 | pollingcnt = 2000; | |
305 | do { | |
306 | cpustatus = rtl_read_byte(rtlpriv, TCR); | |
307 | if (cpustatus & FWRDY) | |
308 | break; | |
309 | udelay(40); | |
310 | } while (pollingcnt--); | |
311 | ||
312 | RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, | |
f30d7507 JP |
313 | "Polling Load Firmware ready, cpustatus(%x)\n", |
314 | cpustatus); | |
701307a8 CL |
315 | |
316 | if (((cpustatus & LOAD_FW_READY) != LOAD_FW_READY) || | |
317 | (pollingcnt <= 0)) { | |
318 | RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, | |
f30d7507 JP |
319 | "Polling Load Firmware ready fail ! cpustatus(%x)\n", |
320 | cpustatus); | |
701307a8 CL |
321 | goto status_check_fail; |
322 | } | |
323 | ||
324 | /* If right here, we can set TCR/RCR to desired value */ | |
325 | /* and config MAC lookback mode to normal mode */ | |
326 | tmpu4b = rtl_read_dword(rtlpriv, TCR); | |
327 | rtl_write_dword(rtlpriv, TCR, (tmpu4b & (~TCR_ICV))); | |
328 | ||
329 | tmpu4b = rtl_read_dword(rtlpriv, RCR); | |
330 | rtl_write_dword(rtlpriv, RCR, (tmpu4b | RCR_APPFCS | | |
331 | RCR_APP_ICV | RCR_APP_MIC)); | |
332 | ||
333 | RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, | |
f30d7507 | 334 | "Current RCR settings(%#x)\n", tmpu4b); |
701307a8 CL |
335 | |
336 | /* Set to normal mode. */ | |
337 | rtl_write_byte(rtlpriv, LBKMD_SEL, LBK_NORMAL); | |
338 | break; | |
339 | ||
340 | default: | |
341 | RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, | |
f30d7507 | 342 | "Unknown status check!\n"); |
701307a8 CL |
343 | rtstatus = false; |
344 | break; | |
345 | } | |
346 | ||
347 | status_check_fail: | |
f30d7507 JP |
348 | RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, |
349 | "loadfw_status(%d), rtstatus(%x)\n", | |
350 | loadfw_status, rtstatus); | |
701307a8 CL |
351 | return rtstatus; |
352 | } | |
353 | ||
354 | int rtl92s_download_fw(struct ieee80211_hw *hw) | |
355 | { | |
356 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
357 | struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); | |
358 | struct rt_firmware *firmware = NULL; | |
359 | struct fw_hdr *pfwheader; | |
360 | struct fw_priv *pfw_priv = NULL; | |
361 | u8 *puc_mappedfile = NULL; | |
362 | u32 ul_filelength = 0; | |
701307a8 CL |
363 | u8 fwhdr_size = RT_8192S_FIRMWARE_HDR_SIZE; |
364 | u8 fwstatus = FW_STATUS_INIT; | |
365 | bool rtstatus = true; | |
366 | ||
b0302aba | 367 | if (rtlpriv->max_fw_size == 0 || !rtlhal->pfirmware) |
701307a8 CL |
368 | return 1; |
369 | ||
370 | firmware = (struct rt_firmware *)rtlhal->pfirmware; | |
371 | firmware->fwstatus = FW_STATUS_INIT; | |
372 | ||
373 | puc_mappedfile = firmware->sz_fw_tmpbuffer; | |
701307a8 CL |
374 | |
375 | /* 1. Retrieve FW header. */ | |
376 | firmware->pfwheader = (struct fw_hdr *) puc_mappedfile; | |
377 | pfwheader = firmware->pfwheader; | |
378 | firmware->firmwareversion = byte(pfwheader->version, 0); | |
379 | firmware->pfwheader->fwpriv.hci_sel = 1;/* pcie */ | |
380 | ||
f30d7507 JP |
381 | RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, |
382 | "signature:%x, version:%x, size:%x, imemsize:%x, sram size:%x\n", | |
383 | pfwheader->signature, | |
701307a8 | 384 | pfwheader->version, pfwheader->dmem_size, |
f30d7507 | 385 | pfwheader->img_imem_size, pfwheader->img_sram_size); |
701307a8 CL |
386 | |
387 | /* 2. Retrieve IMEM image. */ | |
388 | if ((pfwheader->img_imem_size == 0) || (pfwheader->img_imem_size > | |
389 | sizeof(firmware->fw_imem))) { | |
390 | RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, | |
f30d7507 | 391 | "memory for data image is less than IMEM required\n"); |
701307a8 CL |
392 | goto fail; |
393 | } else { | |
394 | puc_mappedfile += fwhdr_size; | |
395 | ||
396 | memcpy(firmware->fw_imem, puc_mappedfile, | |
397 | pfwheader->img_imem_size); | |
398 | firmware->fw_imem_len = pfwheader->img_imem_size; | |
399 | } | |
400 | ||
401 | /* 3. Retriecve EMEM image. */ | |
402 | if (pfwheader->img_sram_size > sizeof(firmware->fw_emem)) { | |
403 | RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, | |
f30d7507 | 404 | "memory for data image is less than EMEM required\n"); |
701307a8 CL |
405 | goto fail; |
406 | } else { | |
407 | puc_mappedfile += firmware->fw_imem_len; | |
408 | ||
409 | memcpy(firmware->fw_emem, puc_mappedfile, | |
410 | pfwheader->img_sram_size); | |
411 | firmware->fw_emem_len = pfwheader->img_sram_size; | |
412 | } | |
413 | ||
414 | /* 4. download fw now */ | |
415 | fwstatus = _rtl92s_firmware_get_nextstatus(firmware->fwstatus); | |
416 | while (fwstatus != FW_STATUS_READY) { | |
417 | /* Image buffer redirection. */ | |
418 | switch (fwstatus) { | |
419 | case FW_STATUS_LOAD_IMEM: | |
420 | puc_mappedfile = firmware->fw_imem; | |
421 | ul_filelength = firmware->fw_imem_len; | |
422 | break; | |
423 | case FW_STATUS_LOAD_EMEM: | |
424 | puc_mappedfile = firmware->fw_emem; | |
425 | ul_filelength = firmware->fw_emem_len; | |
426 | break; | |
427 | case FW_STATUS_LOAD_DMEM: | |
428 | /* Partial update the content of header private. */ | |
429 | pfwheader = firmware->pfwheader; | |
430 | pfw_priv = &pfwheader->fwpriv; | |
431 | _rtl92s_firmwareheader_priveupdate(hw, pfw_priv); | |
432 | puc_mappedfile = (u8 *)(firmware->pfwheader) + | |
433 | RT_8192S_FIRMWARE_HDR_EXCLUDE_PRI_SIZE; | |
434 | ul_filelength = fwhdr_size - | |
435 | RT_8192S_FIRMWARE_HDR_EXCLUDE_PRI_SIZE; | |
436 | break; | |
437 | default: | |
438 | RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, | |
f30d7507 | 439 | "Unexpected Download step!!\n"); |
701307a8 CL |
440 | goto fail; |
441 | break; | |
442 | } | |
443 | ||
444 | /* <2> Download image file */ | |
445 | rtstatus = _rtl92s_firmware_downloadcode(hw, puc_mappedfile, | |
446 | ul_filelength); | |
447 | ||
23677ce3 | 448 | if (!rtstatus) { |
f30d7507 | 449 | RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "fail!\n"); |
701307a8 CL |
450 | goto fail; |
451 | } | |
452 | ||
453 | /* <3> Check whether load FW process is ready */ | |
454 | rtstatus = _rtl92s_firmware_checkready(hw, fwstatus); | |
23677ce3 | 455 | if (!rtstatus) { |
f30d7507 | 456 | RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "fail!\n"); |
701307a8 CL |
457 | goto fail; |
458 | } | |
459 | ||
460 | fwstatus = _rtl92s_firmware_get_nextstatus(firmware->fwstatus); | |
461 | } | |
462 | ||
463 | return rtstatus; | |
464 | fail: | |
465 | return 0; | |
466 | } | |
467 | ||
468 | static u32 _rtl92s_fill_h2c_cmd(struct sk_buff *skb, u32 h2cbufferlen, | |
469 | u32 cmd_num, u32 *pelement_id, u32 *pcmd_len, | |
470 | u8 **pcmb_buffer, u8 *cmd_start_seq) | |
471 | { | |
472 | u32 totallen = 0, len = 0, tx_desclen = 0; | |
473 | u32 pre_continueoffset = 0; | |
474 | u8 *ph2c_buffer; | |
475 | u8 i = 0; | |
476 | ||
477 | do { | |
478 | /* 8 - Byte aligment */ | |
479 | len = H2C_TX_CMD_HDR_LEN + N_BYTE_ALIGMENT(pcmd_len[i], 8); | |
480 | ||
481 | /* Buffer length is not enough */ | |
482 | if (h2cbufferlen < totallen + len + tx_desclen) | |
483 | break; | |
484 | ||
485 | /* Clear content */ | |
486 | ph2c_buffer = (u8 *)skb_put(skb, (u32)len); | |
487 | memset((ph2c_buffer + totallen + tx_desclen), 0, len); | |
488 | ||
489 | /* CMD len */ | |
490 | SET_BITS_TO_LE_4BYTE((ph2c_buffer + totallen + tx_desclen), | |
491 | 0, 16, pcmd_len[i]); | |
492 | ||
493 | /* CMD ID */ | |
494 | SET_BITS_TO_LE_4BYTE((ph2c_buffer + totallen + tx_desclen), | |
495 | 16, 8, pelement_id[i]); | |
496 | ||
497 | /* CMD Sequence */ | |
498 | *cmd_start_seq = *cmd_start_seq % 0x80; | |
499 | SET_BITS_TO_LE_4BYTE((ph2c_buffer + totallen + tx_desclen), | |
500 | 24, 7, *cmd_start_seq); | |
501 | ++*cmd_start_seq; | |
502 | ||
503 | /* Copy memory */ | |
504 | memcpy((ph2c_buffer + totallen + tx_desclen + | |
505 | H2C_TX_CMD_HDR_LEN), pcmb_buffer[i], pcmd_len[i]); | |
506 | ||
507 | /* CMD continue */ | |
508 | /* set the continue in prevoius cmd. */ | |
509 | if (i < cmd_num - 1) | |
510 | SET_BITS_TO_LE_4BYTE((ph2c_buffer + pre_continueoffset), | |
511 | 31, 1, 1); | |
512 | ||
513 | pre_continueoffset = totallen; | |
514 | ||
515 | totallen += len; | |
516 | } while (++i < cmd_num); | |
517 | ||
518 | return totallen; | |
519 | } | |
520 | ||
521 | static u32 _rtl92s_get_h2c_cmdlen(u32 h2cbufferlen, u32 cmd_num, u32 *pcmd_len) | |
522 | { | |
523 | u32 totallen = 0, len = 0, tx_desclen = 0; | |
524 | u8 i = 0; | |
525 | ||
526 | do { | |
527 | /* 8 - Byte aligment */ | |
528 | len = H2C_TX_CMD_HDR_LEN + N_BYTE_ALIGMENT(pcmd_len[i], 8); | |
529 | ||
530 | /* Buffer length is not enough */ | |
531 | if (h2cbufferlen < totallen + len + tx_desclen) | |
532 | break; | |
533 | ||
534 | totallen += len; | |
535 | } while (++i < cmd_num); | |
536 | ||
537 | return totallen + tx_desclen; | |
538 | } | |
539 | ||
540 | static bool _rtl92s_firmware_set_h2c_cmd(struct ieee80211_hw *hw, u8 h2c_cmd, | |
541 | u8 *pcmd_buffer) | |
542 | { | |
543 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
544 | struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); | |
545 | struct rtl_tcb_desc *cb_desc; | |
546 | struct sk_buff *skb; | |
547 | u32 element_id = 0; | |
548 | u32 cmd_len = 0; | |
549 | u32 len; | |
550 | ||
551 | switch (h2c_cmd) { | |
552 | case FW_H2C_SETPWRMODE: | |
553 | element_id = H2C_SETPWRMODE_CMD ; | |
554 | cmd_len = sizeof(struct h2c_set_pwrmode_parm); | |
555 | break; | |
556 | case FW_H2C_JOINBSSRPT: | |
557 | element_id = H2C_JOINBSSRPT_CMD; | |
558 | cmd_len = sizeof(struct h2c_joinbss_rpt_parm); | |
559 | break; | |
560 | case FW_H2C_WOWLAN_UPDATE_GTK: | |
561 | element_id = H2C_WOWLAN_UPDATE_GTK_CMD; | |
562 | cmd_len = sizeof(struct h2c_wpa_two_way_parm); | |
563 | break; | |
564 | case FW_H2C_WOWLAN_UPDATE_IV: | |
565 | element_id = H2C_WOWLAN_UPDATE_IV_CMD; | |
566 | cmd_len = sizeof(unsigned long long); | |
567 | break; | |
568 | case FW_H2C_WOWLAN_OFFLOAD: | |
569 | element_id = H2C_WOWLAN_FW_OFFLOAD; | |
570 | cmd_len = sizeof(u8); | |
571 | break; | |
572 | default: | |
573 | break; | |
574 | } | |
575 | ||
576 | len = _rtl92s_get_h2c_cmdlen(MAX_TRANSMIT_BUFFER_SIZE, 1, &cmd_len); | |
577 | skb = dev_alloc_skb(len); | |
d90db4b1 LF |
578 | if (!skb) |
579 | return false; | |
701307a8 CL |
580 | cb_desc = (struct rtl_tcb_desc *)(skb->cb); |
581 | cb_desc->queue_index = TXCMD_QUEUE; | |
582 | cb_desc->cmd_or_init = DESC_PACKET_TYPE_NORMAL; | |
583 | cb_desc->last_inipkt = false; | |
584 | ||
585 | _rtl92s_fill_h2c_cmd(skb, MAX_TRANSMIT_BUFFER_SIZE, 1, &element_id, | |
586 | &cmd_len, &pcmd_buffer, &rtlhal->h2c_txcmd_seq); | |
587 | _rtl92s_cmd_send_packet(hw, skb, false); | |
588 | rtlpriv->cfg->ops->tx_polling(hw, TXCMD_QUEUE); | |
589 | ||
590 | return true; | |
591 | } | |
592 | ||
593 | void rtl92s_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 Mode) | |
594 | { | |
595 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | |
596 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | |
597 | struct h2c_set_pwrmode_parm pwrmode; | |
598 | u16 max_wakeup_period = 0; | |
599 | ||
600 | pwrmode.mode = Mode; | |
601 | pwrmode.flag_low_traffic_en = 0; | |
602 | pwrmode.flag_lpnav_en = 0; | |
603 | pwrmode.flag_rf_low_snr_en = 0; | |
604 | pwrmode.flag_dps_en = 0; | |
605 | pwrmode.bcn_rx_en = 0; | |
606 | pwrmode.bcn_to = 0; | |
607 | SET_BITS_TO_LE_2BYTE((u8 *)(&pwrmode) + 8, 0, 16, | |
608 | mac->vif->bss_conf.beacon_int); | |
609 | pwrmode.app_itv = 0; | |
610 | pwrmode.awake_bcn_itvl = ppsc->reg_max_lps_awakeintvl; | |
611 | pwrmode.smart_ps = 1; | |
612 | pwrmode.bcn_pass_period = 10; | |
613 | ||
614 | /* Set beacon pass count */ | |
615 | if (pwrmode.mode == FW_PS_MIN_MODE) | |
616 | max_wakeup_period = mac->vif->bss_conf.beacon_int; | |
617 | else if (pwrmode.mode == FW_PS_MAX_MODE) | |
618 | max_wakeup_period = mac->vif->bss_conf.beacon_int * | |
619 | mac->vif->bss_conf.dtim_period; | |
620 | ||
621 | if (max_wakeup_period >= 500) | |
622 | pwrmode.bcn_pass_cnt = 1; | |
623 | else if ((max_wakeup_period >= 300) && (max_wakeup_period < 500)) | |
624 | pwrmode.bcn_pass_cnt = 2; | |
625 | else if ((max_wakeup_period >= 200) && (max_wakeup_period < 300)) | |
626 | pwrmode.bcn_pass_cnt = 3; | |
627 | else if ((max_wakeup_period >= 20) && (max_wakeup_period < 200)) | |
628 | pwrmode.bcn_pass_cnt = 5; | |
629 | else | |
630 | pwrmode.bcn_pass_cnt = 1; | |
631 | ||
632 | _rtl92s_firmware_set_h2c_cmd(hw, FW_H2C_SETPWRMODE, (u8 *)&pwrmode); | |
633 | ||
634 | } | |
635 | ||
636 | void rtl92s_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, | |
637 | u8 mstatus, u8 ps_qosinfo) | |
638 | { | |
639 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | |
640 | struct h2c_joinbss_rpt_parm joinbss_rpt; | |
641 | ||
642 | joinbss_rpt.opmode = mstatus; | |
643 | joinbss_rpt.ps_qos_info = ps_qosinfo; | |
644 | joinbss_rpt.bssid[0] = mac->bssid[0]; | |
645 | joinbss_rpt.bssid[1] = mac->bssid[1]; | |
646 | joinbss_rpt.bssid[2] = mac->bssid[2]; | |
647 | joinbss_rpt.bssid[3] = mac->bssid[3]; | |
648 | joinbss_rpt.bssid[4] = mac->bssid[4]; | |
649 | joinbss_rpt.bssid[5] = mac->bssid[5]; | |
650 | SET_BITS_TO_LE_2BYTE((u8 *)(&joinbss_rpt) + 8, 0, 16, | |
651 | mac->vif->bss_conf.beacon_int); | |
652 | SET_BITS_TO_LE_2BYTE((u8 *)(&joinbss_rpt) + 10, 0, 16, mac->assoc_id); | |
653 | ||
654 | _rtl92s_firmware_set_h2c_cmd(hw, FW_H2C_JOINBSSRPT, (u8 *)&joinbss_rpt); | |
655 | } | |
656 |