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