]>
Commit | Line | Data |
---|---|---|
aa45a673 LF |
1 | /****************************************************************************** |
2 | * | |
3 | * Copyright(c) 2009-2014 Realtek Corporation. | |
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 | * 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" | |
cbd0c851 LF |
27 | #include "../pci.h" |
28 | #include "../base.h" | |
aa45a673 LF |
29 | #include "fw_common.h" |
30 | #include <linux/module.h> | |
31 | ||
aa45a673 LF |
32 | void rtl8723_enable_fw_download(struct ieee80211_hw *hw, bool enable) |
33 | { | |
34 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
35 | u8 tmp; | |
36 | ||
37 | if (enable) { | |
38 | tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); | |
0529c6b8 LF |
39 | rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, |
40 | tmp | 0x04); | |
aa45a673 LF |
41 | |
42 | tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL); | |
43 | rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp | 0x01); | |
44 | ||
45 | tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL + 2); | |
46 | rtl_write_byte(rtlpriv, REG_MCUFWDL + 2, tmp & 0xf7); | |
47 | } else { | |
48 | tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL); | |
49 | rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp & 0xfe); | |
50 | ||
51 | rtl_write_byte(rtlpriv, REG_MCUFWDL + 1, 0x00); | |
52 | } | |
53 | } | |
54 | EXPORT_SYMBOL_GPL(rtl8723_enable_fw_download); | |
55 | ||
56 | void rtl8723_fw_block_write(struct ieee80211_hw *hw, | |
57 | const u8 *buffer, u32 size) | |
58 | { | |
59 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
60 | u32 blocksize = sizeof(u32); | |
61 | u8 *bufferptr = (u8 *)buffer; | |
62 | u32 *pu4byteptr = (u32 *)buffer; | |
63 | u32 i, offset, blockcount, remainsize; | |
64 | ||
65 | blockcount = size / blocksize; | |
66 | remainsize = size % blocksize; | |
67 | ||
68 | for (i = 0; i < blockcount; i++) { | |
69 | offset = i * blocksize; | |
70 | rtl_write_dword(rtlpriv, (FW_8192C_START_ADDRESS + offset), | |
71 | *(pu4byteptr + i)); | |
72 | } | |
73 | if (remainsize) { | |
74 | offset = blockcount * blocksize; | |
75 | bufferptr += offset; | |
76 | for (i = 0; i < remainsize; i++) { | |
77 | rtl_write_byte(rtlpriv, | |
78 | (FW_8192C_START_ADDRESS + offset + i), | |
79 | *(bufferptr + i)); | |
80 | } | |
81 | } | |
82 | } | |
83 | EXPORT_SYMBOL_GPL(rtl8723_fw_block_write); | |
84 | ||
85 | void rtl8723_fw_page_write(struct ieee80211_hw *hw, | |
86 | u32 page, const u8 *buffer, u32 size) | |
87 | { | |
88 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
89 | u8 value8; | |
90 | u8 u8page = (u8) (page & 0x07); | |
91 | ||
92 | value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page; | |
93 | ||
94 | rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8); | |
95 | rtl8723_fw_block_write(hw, buffer, size); | |
96 | } | |
97 | EXPORT_SYMBOL_GPL(rtl8723_fw_page_write); | |
98 | ||
0529c6b8 | 99 | void rtl8723_fill_dummy(u8 *pfwbuf, u32 *pfwlen) |
aa45a673 LF |
100 | { |
101 | u32 fwlen = *pfwlen; | |
102 | u8 remain = (u8) (fwlen % 4); | |
103 | ||
104 | remain = (remain == 0) ? 0 : (4 - remain); | |
105 | ||
106 | while (remain > 0) { | |
107 | pfwbuf[fwlen] = 0; | |
108 | fwlen++; | |
109 | remain--; | |
110 | } | |
111 | *pfwlen = fwlen; | |
112 | } | |
0529c6b8 | 113 | EXPORT_SYMBOL(rtl8723_fill_dummy); |
aa45a673 LF |
114 | |
115 | void rtl8723_write_fw(struct ieee80211_hw *hw, | |
cbd0c851 | 116 | enum version_8723e version, |
0529c6b8 | 117 | u8 *buffer, u32 size, u8 max_page) |
aa45a673 LF |
118 | { |
119 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
1851cb4a | 120 | u8 *bufferptr = buffer; |
0529c6b8 | 121 | u32 page_nums, remain_size; |
aa45a673 LF |
122 | u32 page, offset; |
123 | ||
0529c6b8 | 124 | RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "FW size is %d bytes,\n", size); |
aa45a673 | 125 | |
cbd0c851 | 126 | rtl8723_fill_dummy(bufferptr, &size); |
aa45a673 | 127 | |
0529c6b8 LF |
128 | page_nums = size / FW_8192C_PAGE_SIZE; |
129 | remain_size = size % FW_8192C_PAGE_SIZE; | |
aa45a673 | 130 | |
0529c6b8 | 131 | if (page_nums > max_page) { |
c7532b87 LF |
132 | pr_err("Page numbers should not greater than %d\n", |
133 | max_page); | |
aa45a673 | 134 | } |
0529c6b8 | 135 | for (page = 0; page < page_nums; page++) { |
aa45a673 LF |
136 | offset = page * FW_8192C_PAGE_SIZE; |
137 | rtl8723_fw_page_write(hw, page, (bufferptr + offset), | |
138 | FW_8192C_PAGE_SIZE); | |
139 | } | |
0529c6b8 LF |
140 | |
141 | if (remain_size) { | |
142 | offset = page_nums * FW_8192C_PAGE_SIZE; | |
143 | page = page_nums; | |
aa45a673 | 144 | rtl8723_fw_page_write(hw, page, (bufferptr + offset), |
0529c6b8 | 145 | remain_size); |
aa45a673 | 146 | } |
0529c6b8 | 147 | RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "FW write done.\n"); |
aa45a673 LF |
148 | } |
149 | EXPORT_SYMBOL_GPL(rtl8723_write_fw); | |
150 | ||
151 | void rtl8723ae_firmware_selfreset(struct ieee80211_hw *hw) | |
152 | { | |
0529c6b8 | 153 | u8 u1b_tmp; |
aa45a673 LF |
154 | u8 delay = 100; |
155 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
156 | ||
157 | rtl_write_byte(rtlpriv, REG_HMETFR + 3, 0x20); | |
0529c6b8 | 158 | u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); |
aa45a673 | 159 | |
0529c6b8 | 160 | while (u1b_tmp & BIT(2)) { |
aa45a673 LF |
161 | delay--; |
162 | if (delay == 0) | |
163 | break; | |
164 | udelay(50); | |
0529c6b8 | 165 | u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); |
aa45a673 LF |
166 | } |
167 | if (delay == 0) { | |
0529c6b8 LF |
168 | u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); |
169 | rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, | |
170 | u1b_tmp&(~BIT(2))); | |
aa45a673 LF |
171 | } |
172 | } | |
173 | EXPORT_SYMBOL_GPL(rtl8723ae_firmware_selfreset); | |
174 | ||
175 | void rtl8723be_firmware_selfreset(struct ieee80211_hw *hw) | |
176 | { | |
177 | u8 u1b_tmp; | |
178 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
179 | ||
180 | u1b_tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL + 1); | |
181 | rtl_write_byte(rtlpriv, REG_RSV_CTRL + 1, (u1b_tmp & (~BIT(0)))); | |
182 | ||
183 | u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); | |
184 | rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, (u1b_tmp & (~BIT(2)))); | |
185 | udelay(50); | |
186 | ||
187 | u1b_tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL + 1); | |
188 | rtl_write_byte(rtlpriv, REG_RSV_CTRL + 1, (u1b_tmp | BIT(0))); | |
189 | ||
190 | u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); | |
191 | rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, (u1b_tmp | BIT(2))); | |
192 | ||
193 | RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, | |
194 | " _8051Reset8723be(): 8051 reset success .\n"); | |
195 | } | |
196 | EXPORT_SYMBOL_GPL(rtl8723be_firmware_selfreset); | |
197 | ||
0529c6b8 LF |
198 | int rtl8723_fw_free_to_go(struct ieee80211_hw *hw, bool is_8723be, |
199 | int max_count) | |
aa45a673 LF |
200 | { |
201 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
202 | int err = -EIO; | |
203 | u32 counter = 0; | |
204 | u32 value32; | |
205 | ||
206 | do { | |
207 | value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL); | |
0529c6b8 | 208 | } while ((counter++ < max_count) && |
aa45a673 LF |
209 | (!(value32 & FWDL_CHKSUM_RPT))); |
210 | ||
0529c6b8 | 211 | if (counter >= max_count) { |
c7532b87 LF |
212 | pr_err("chksum report fail ! REG_MCUFWDL:0x%08x .\n", |
213 | value32); | |
aa45a673 LF |
214 | goto exit; |
215 | } | |
216 | RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, | |
217 | "Checksum report OK ! REG_MCUFWDL:0x%08x .\n", value32); | |
218 | ||
cbd0c851 | 219 | value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL) | MCUFWDL_RDY; |
aa45a673 LF |
220 | value32 &= ~WINTINI_RDY; |
221 | rtl_write_dword(rtlpriv, REG_MCUFWDL, value32); | |
222 | ||
223 | if (is_8723be) | |
224 | rtl8723be_firmware_selfreset(hw); | |
225 | counter = 0; | |
226 | ||
227 | do { | |
228 | value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL); | |
229 | if (value32 & WINTINI_RDY) { | |
230 | RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, | |
0529c6b8 | 231 | "Polling FW ready success!! REG_MCUFWDL:0x%08x .\n", |
aa45a673 LF |
232 | value32); |
233 | err = 0; | |
234 | goto exit; | |
235 | } | |
aa45a673 | 236 | |
0529c6b8 LF |
237 | mdelay(FW_8192C_POLLING_DELAY); |
238 | ||
239 | } while (counter++ < max_count); | |
aa45a673 | 240 | |
c7532b87 LF |
241 | pr_err("Polling FW ready fail!! REG_MCUFWDL:0x%08x .\n", |
242 | value32); | |
aa45a673 LF |
243 | |
244 | exit: | |
245 | return err; | |
246 | } | |
247 | EXPORT_SYMBOL_GPL(rtl8723_fw_free_to_go); | |
248 | ||
249 | int rtl8723_download_fw(struct ieee80211_hw *hw, | |
0529c6b8 | 250 | bool is_8723be, int max_count) |
aa45a673 LF |
251 | { |
252 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
253 | struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); | |
7c24d086 | 254 | struct rtlwifi_firmware_header *pfwheader; |
aa45a673 LF |
255 | u8 *pfwdata; |
256 | u32 fwsize; | |
257 | int err; | |
258 | enum version_8723e version = rtlhal->version; | |
0529c6b8 | 259 | int max_page; |
aa45a673 LF |
260 | |
261 | if (!rtlhal->pfirmware) | |
262 | return 1; | |
263 | ||
7c24d086 | 264 | pfwheader = (struct rtlwifi_firmware_header *)rtlhal->pfirmware; |
1851cb4a | 265 | pfwdata = rtlhal->pfirmware; |
aa45a673 | 266 | fwsize = rtlhal->fwsize; |
aa45a673 | 267 | |
5c99f04f | 268 | if (!is_8723be) |
0529c6b8 LF |
269 | max_page = 6; |
270 | else | |
271 | max_page = 8; | |
cbd0c851 | 272 | if (rtlpriv->cfg->ops->is_fw_header(pfwheader)) { |
1bae2ae3 | 273 | RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, |
aa45a673 LF |
274 | "Firmware Version(%d), Signature(%#x), Size(%d)\n", |
275 | pfwheader->version, pfwheader->signature, | |
7c24d086 | 276 | (int)sizeof(struct rtlwifi_firmware_header)); |
aa45a673 | 277 | |
7c24d086 LF |
278 | pfwdata = pfwdata + sizeof(struct rtlwifi_firmware_header); |
279 | fwsize = fwsize - sizeof(struct rtlwifi_firmware_header); | |
aa45a673 | 280 | } |
0529c6b8 LF |
281 | |
282 | if (rtl_read_byte(rtlpriv, REG_MCUFWDL)&BIT(7)) { | |
aa45a673 LF |
283 | if (is_8723be) |
284 | rtl8723be_firmware_selfreset(hw); | |
285 | else | |
286 | rtl8723ae_firmware_selfreset(hw); | |
0529c6b8 | 287 | rtl_write_byte(rtlpriv, REG_MCUFWDL, 0x00); |
aa45a673 | 288 | } |
cbd0c851 | 289 | rtl8723_enable_fw_download(hw, true); |
0529c6b8 | 290 | rtl8723_write_fw(hw, version, pfwdata, fwsize, max_page); |
cbd0c851 | 291 | rtl8723_enable_fw_download(hw, false); |
aa45a673 | 292 | |
0529c6b8 | 293 | err = rtl8723_fw_free_to_go(hw, is_8723be, max_count); |
aa45a673 | 294 | if (err) { |
c7532b87 | 295 | pr_err("Firmware is not ready to run!\n"); |
aa45a673 | 296 | } else { |
0529c6b8 | 297 | RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, |
aa45a673 LF |
298 | "Firmware is ready to run!\n"); |
299 | } | |
300 | return 0; | |
301 | } | |
302 | EXPORT_SYMBOL_GPL(rtl8723_download_fw); | |
303 | ||
aa45a673 LF |
304 | bool rtl8723_cmd_send_packet(struct ieee80211_hw *hw, |
305 | struct sk_buff *skb) | |
306 | { | |
307 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
308 | struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); | |
309 | struct rtl8192_tx_ring *ring; | |
310 | struct rtl_tx_desc *pdesc; | |
311 | struct sk_buff *pskb = NULL; | |
312 | u8 own; | |
313 | unsigned long flags; | |
314 | ||
315 | ring = &rtlpci->tx_ring[BEACON_QUEUE]; | |
316 | ||
317 | pskb = __skb_dequeue(&ring->queue); | |
073d72f9 | 318 | kfree_skb(pskb); |
aa45a673 LF |
319 | spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); |
320 | ||
321 | pdesc = &ring->desc[0]; | |
322 | own = (u8) rtlpriv->cfg->ops->get_desc((u8 *)pdesc, true, HW_DESC_OWN); | |
323 | ||
324 | rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *)pdesc, 1, 1, skb); | |
325 | ||
326 | __skb_queue_tail(&ring->queue, skb); | |
327 | ||
328 | spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags); | |
329 | ||
330 | rtlpriv->cfg->ops->tx_polling(hw, BEACON_QUEUE); | |
331 | ||
332 | return true; | |
333 | } | |
334 | EXPORT_SYMBOL_GPL(rtl8723_cmd_send_packet); |