]>
Commit | Line | Data |
---|---|---|
2865d42c LF |
1 | /****************************************************************************** |
2 | * hal_init.c | |
3 | * | |
4 | * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. | |
5 | * Linux device driver for RTL8192SU | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of version 2 of the GNU General Public License as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along with | |
17 | * this program; if not, write to the Free Software Foundation, Inc., | |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA | |
19 | * | |
20 | * Modifications for inclusion into the Linux staging tree are | |
21 | * Copyright(c) 2010 Larry Finger. All rights reserved. | |
22 | * | |
23 | * Contact information: | |
24 | * WLAN FAE <wlanfae@realtek.com>. | |
25 | * Larry Finger <Larry.Finger@lwfinger.net> | |
26 | * | |
27 | ******************************************************************************/ | |
28 | ||
29 | #define _HAL_INIT_C_ | |
30 | ||
359140aa AB |
31 | #include <linux/usb.h> |
32 | #include <linux/device.h> | |
33 | #include <linux/usb/ch9.h> | |
34 | #include <linux/firmware.h> | |
81e07c06 | 35 | #include <linux/module.h> |
359140aa | 36 | |
2865d42c LF |
37 | #include "osdep_service.h" |
38 | #include "drv_types.h" | |
16e53729 | 39 | #include "usb_osintf.h" |
2865d42c LF |
40 | |
41 | #define FWBUFF_ALIGN_SZ 512 | |
42 | #define MAX_DUMP_FWSZ 49152 /*default = 49152 (48k)*/ | |
43 | ||
8c213fa5 | 44 | static void rtl871x_load_fw_cb(const struct firmware *firmware, void *context) |
2865d42c | 45 | { |
8c213fa5 LF |
46 | struct _adapter *padapter = context; |
47 | ||
48 | complete(&padapter->rtl8712_fw_ready); | |
49 | if (!firmware) { | |
50 | struct usb_device *udev = padapter->dvobjpriv.pusbdev; | |
51 | struct usb_interface *pusb_intf = padapter->pusb_intf; | |
6a7cbc38 | 52 | |
87a573ad | 53 | dev_err(&udev->dev, "r8712u: Firmware request failed\n"); |
8c213fa5 LF |
54 | usb_put_dev(udev); |
55 | usb_set_intfdata(pusb_intf, NULL); | |
56 | return; | |
57 | } | |
58 | padapter->fw = firmware; | |
8c213fa5 LF |
59 | /* firmware available - start netdev */ |
60 | register_netdev(padapter->pnetdev); | |
61 | } | |
62 | ||
63 | static const char firmware_file[] = "rtlwifi/rtl8712u.bin"; | |
64 | ||
65 | int rtl871x_load_fw(struct _adapter *padapter) | |
66 | { | |
67 | struct device *dev = &padapter->dvobjpriv.pusbdev->dev; | |
c84a7028 | 68 | int rc; |
2865d42c | 69 | |
8c213fa5 | 70 | init_completion(&padapter->rtl8712_fw_ready); |
87a573ad | 71 | dev_info(dev, "r8712u: Loading firmware from \"%s\"\n", firmware_file); |
8c213fa5 LF |
72 | rc = request_firmware_nowait(THIS_MODULE, 1, firmware_file, dev, |
73 | GFP_KERNEL, padapter, rtl871x_load_fw_cb); | |
74 | if (rc) | |
87a573ad | 75 | dev_err(dev, "r8712u: Firmware request error %d\n", rc); |
8c213fa5 LF |
76 | return rc; |
77 | } | |
78 | MODULE_FIRMWARE("rtlwifi/rtl8712u.bin"); | |
79 | ||
80 | static u32 rtl871x_open_fw(struct _adapter *padapter, const u8 **ppmappedfw) | |
81 | { | |
82 | const struct firmware **praw = &padapter->fw; | |
83 | ||
84 | if (padapter->fw->size > 200000) { | |
87a573ad PF |
85 | dev_err(&padapter->pnetdev->dev, "r8172u: Badfw->size of %d\n", |
86 | (int)padapter->fw->size); | |
c84a7028 LF |
87 | return 0; |
88 | } | |
eecc515a | 89 | *ppmappedfw = (*praw)->data; |
c84a7028 | 90 | return (*praw)->size; |
2865d42c LF |
91 | } |
92 | ||
93 | static void fill_fwpriv(struct _adapter *padapter, struct fw_priv *pfwpriv) | |
94 | { | |
8d0514d5 | 95 | struct dvobj_priv *pdvobj = &padapter->dvobjpriv; |
2865d42c LF |
96 | struct registry_priv *pregpriv = &padapter->registrypriv; |
97 | ||
98 | memset(pfwpriv, 0, sizeof(struct fw_priv)); | |
99 | /* todo: check if needs endian conversion */ | |
100 | pfwpriv->hci_sel = RTL8712_HCI_TYPE_72USB; | |
101 | pfwpriv->usb_ep_num = (u8)pdvobj->nr_endpoint; | |
102 | pfwpriv->bw_40MHz_en = pregpriv->cbw40_enable; | |
103 | switch (pregpriv->rf_config) { | |
104 | case RTL8712_RF_1T1R: | |
105 | pfwpriv->rf_config = RTL8712_RFC_1T1R; | |
106 | break; | |
107 | case RTL8712_RF_2T2R: | |
108 | pfwpriv->rf_config = RTL8712_RFC_2T2R; | |
109 | break; | |
110 | case RTL8712_RF_1T2R: | |
111 | default: | |
112 | pfwpriv->rf_config = RTL8712_RFC_1T2R; | |
113 | } | |
114 | pfwpriv->mp_mode = (pregpriv->mp_mode == 1) ? 1 : 0; | |
115 | pfwpriv->vcsType = pregpriv->vrtl_carrier_sense; /* 0:off 1:on 2:auto */ | |
116 | pfwpriv->vcsMode = pregpriv->vcs_type; /* 1:RTS/CTS 2:CTS to self */ | |
d19b8647 LF |
117 | /* default enable turboMode */ |
118 | pfwpriv->turboMode = ((pregpriv->wifi_test == 1) ? 0 : 1); | |
2865d42c LF |
119 | pfwpriv->lowPowerMode = pregpriv->low_power; |
120 | } | |
121 | ||
122 | static void update_fwhdr(struct fw_hdr *pfwhdr, const u8 *pmappedfw) | |
123 | { | |
124 | pfwhdr->signature = le16_to_cpu(*(u16 *)pmappedfw); | |
b3845d02 | 125 | pfwhdr->version = le16_to_cpu(*(u16 *)(pmappedfw + 2)); |
2865d42c | 126 | /* define the size of boot loader */ |
b3845d02 | 127 | pfwhdr->dmem_size = le32_to_cpu(*(uint *)(pmappedfw + 4)); |
2865d42c | 128 | /* define the size of FW in IMEM */ |
b3845d02 | 129 | pfwhdr->img_IMEM_size = le32_to_cpu(*(uint *)(pmappedfw + 8)); |
2865d42c | 130 | /* define the size of FW in SRAM */ |
b3845d02 | 131 | pfwhdr->img_SRAM_size = le32_to_cpu(*(uint *)(pmappedfw + 12)); |
2865d42c | 132 | /* define the size of DMEM variable */ |
b3845d02 | 133 | pfwhdr->fw_priv_sz = le32_to_cpu(*(uint *)(pmappedfw + 16)); |
2865d42c LF |
134 | } |
135 | ||
136 | static u8 chk_fwhdr(struct fw_hdr *pfwhdr, u32 ulfilelength) | |
137 | { | |
138 | u32 fwhdrsz, fw_sz; | |
2865d42c LF |
139 | |
140 | /* check signature */ | |
141 | if ((pfwhdr->signature != 0x8712) && (pfwhdr->signature != 0x8192)) | |
142 | return _FAIL; | |
2865d42c LF |
143 | /* check fw_priv_sze & sizeof(struct fw_priv) */ |
144 | if (pfwhdr->fw_priv_sz != sizeof(struct fw_priv)) | |
145 | return _FAIL; | |
146 | /* check fw_sz & image_fw_sz */ | |
147 | fwhdrsz = FIELD_OFFSET(struct fw_hdr, fwpriv) + pfwhdr->fw_priv_sz; | |
148 | fw_sz = fwhdrsz + pfwhdr->img_IMEM_size + pfwhdr->img_SRAM_size + | |
149 | pfwhdr->dmem_size; | |
150 | if (fw_sz != ulfilelength) | |
151 | return _FAIL; | |
152 | return _SUCCESS; | |
153 | } | |
154 | ||
155 | static u8 rtl8712_dl_fw(struct _adapter *padapter) | |
156 | { | |
157 | sint i; | |
158 | u8 tmp8, tmp8_a; | |
159 | u16 tmp16; | |
e29d3ebc | 160 | u32 maxlen = 0; /* for compare usage */ |
2865d42c LF |
161 | uint dump_imem_sz, imem_sz, dump_emem_sz, emem_sz; /* max = 49152; */ |
162 | struct fw_hdr fwhdr; | |
163 | u32 ulfilelength; /* FW file size */ | |
2865d42c LF |
164 | const u8 *pmappedfw = NULL; |
165 | u8 *ptmpchar = NULL, *ppayload, *ptr; | |
166 | struct tx_desc *ptx_desc; | |
167 | u32 txdscp_sz = sizeof(struct tx_desc); | |
e375870b | 168 | u8 ret = _FAIL; |
2865d42c | 169 | |
8c213fa5 | 170 | ulfilelength = rtl871x_open_fw(padapter, &pmappedfw); |
2865d42c LF |
171 | if (pmappedfw && (ulfilelength > 0)) { |
172 | update_fwhdr(&fwhdr, pmappedfw); | |
173 | if (chk_fwhdr(&fwhdr, ulfilelength) == _FAIL) | |
8c213fa5 | 174 | return ret; |
2865d42c LF |
175 | fill_fwpriv(padapter, &fwhdr.fwpriv); |
176 | /* firmware check ok */ | |
177 | maxlen = (fwhdr.img_IMEM_size > fwhdr.img_SRAM_size) ? | |
178 | fwhdr.img_IMEM_size : fwhdr.img_SRAM_size; | |
179 | maxlen += txdscp_sz; | |
b33fc722 | 180 | ptmpchar = kmalloc(maxlen + FWBUFF_ALIGN_SZ, GFP_KERNEL); |
2a128360 | 181 | if (!ptmpchar) |
8c213fa5 | 182 | return ret; |
2865d42c LF |
183 | |
184 | ptx_desc = (struct tx_desc *)(ptmpchar + FWBUFF_ALIGN_SZ - | |
185 | ((addr_t)(ptmpchar) & (FWBUFF_ALIGN_SZ - 1))); | |
186 | ppayload = (u8 *)(ptx_desc) + txdscp_sz; | |
187 | ptr = (u8 *)pmappedfw + FIELD_OFFSET(struct fw_hdr, fwpriv) + | |
188 | fwhdr.fw_priv_sz; | |
189 | /* Download FirmWare */ | |
190 | /* 1. determine IMEM code size and Load IMEM Code Section */ | |
191 | imem_sz = fwhdr.img_IMEM_size; | |
192 | do { | |
193 | memset(ptx_desc, 0, TXDESC_SIZE); | |
168a2c10 | 194 | if (imem_sz > MAX_DUMP_FWSZ/*49152*/) { |
2865d42c | 195 | dump_imem_sz = MAX_DUMP_FWSZ; |
168a2c10 | 196 | } else { |
2865d42c LF |
197 | dump_imem_sz = imem_sz; |
198 | ptx_desc->txdw0 |= cpu_to_le32(BIT(28)); | |
199 | } | |
200 | ptx_desc->txdw0 |= cpu_to_le32(dump_imem_sz & | |
201 | 0x0000ffff); | |
202 | memcpy(ppayload, ptr, dump_imem_sz); | |
203 | r8712_write_mem(padapter, RTL8712_DMA_VOQ, | |
204 | dump_imem_sz + TXDESC_SIZE, | |
205 | (u8 *)ptx_desc); | |
206 | ptr += dump_imem_sz; | |
207 | imem_sz -= dump_imem_sz; | |
208 | } while (imem_sz > 0); | |
209 | i = 10; | |
210 | tmp16 = r8712_read16(padapter, TCR); | |
211 | while (((tmp16 & _IMEM_CODE_DONE) == 0) && (i > 0)) { | |
942eaa86 | 212 | usleep_range(10, 1000); |
2865d42c LF |
213 | tmp16 = r8712_read16(padapter, TCR); |
214 | i--; | |
215 | } | |
216 | if (i == 0 || (tmp16 & _IMEM_CHK_RPT) == 0) | |
217 | goto exit_fail; | |
218 | ||
219 | /* 2.Download EMEM code size and Load EMEM Code Section */ | |
220 | emem_sz = fwhdr.img_SRAM_size; | |
221 | do { | |
222 | memset(ptx_desc, 0, TXDESC_SIZE); | |
223 | if (emem_sz > MAX_DUMP_FWSZ) /* max=48k */ | |
224 | dump_emem_sz = MAX_DUMP_FWSZ; | |
225 | else { | |
226 | dump_emem_sz = emem_sz; | |
227 | ptx_desc->txdw0 |= cpu_to_le32(BIT(28)); | |
228 | } | |
229 | ptx_desc->txdw0 |= cpu_to_le32(dump_emem_sz & | |
230 | 0x0000ffff); | |
231 | memcpy(ppayload, ptr, dump_emem_sz); | |
232 | r8712_write_mem(padapter, RTL8712_DMA_VOQ, | |
b3845d02 | 233 | dump_emem_sz + TXDESC_SIZE, (u8 *)ptx_desc); |
2865d42c LF |
234 | ptr += dump_emem_sz; |
235 | emem_sz -= dump_emem_sz; | |
236 | } while (emem_sz > 0); | |
237 | i = 5; | |
238 | tmp16 = r8712_read16(padapter, TCR); | |
239 | while (((tmp16 & _EMEM_CODE_DONE) == 0) && (i > 0)) { | |
942eaa86 | 240 | usleep_range(10, 1000); |
2865d42c LF |
241 | tmp16 = r8712_read16(padapter, TCR); |
242 | i--; | |
243 | } | |
244 | if (i == 0 || (tmp16 & _EMEM_CHK_RPT) == 0) | |
245 | goto exit_fail; | |
246 | ||
247 | /* 3.Enable CPU */ | |
248 | tmp8 = r8712_read8(padapter, SYS_CLKR); | |
7ab4609a | 249 | r8712_write8(padapter, SYS_CLKR, tmp8 | BIT(2)); |
2865d42c | 250 | tmp8_a = r8712_read8(padapter, SYS_CLKR); |
7ab4609a | 251 | if (tmp8_a != (tmp8 | BIT(2))) |
2865d42c LF |
252 | goto exit_fail; |
253 | ||
254 | tmp8 = r8712_read8(padapter, SYS_FUNC_EN + 1); | |
7ab4609a | 255 | r8712_write8(padapter, SYS_FUNC_EN + 1, tmp8 | BIT(2)); |
2865d42c | 256 | tmp8_a = r8712_read8(padapter, SYS_FUNC_EN + 1); |
7ab4609a | 257 | if (tmp8_a != (tmp8 | BIT(2))) |
2865d42c LF |
258 | goto exit_fail; |
259 | ||
e29d3ebc | 260 | r8712_read32(padapter, TCR); |
2865d42c LF |
261 | |
262 | /* 4.polling IMEM Ready */ | |
263 | i = 100; | |
264 | tmp16 = r8712_read16(padapter, TCR); | |
265 | while (((tmp16 & _IMEM_RDY) == 0) && (i > 0)) { | |
266 | msleep(20); | |
267 | tmp16 = r8712_read16(padapter, TCR); | |
268 | i--; | |
269 | } | |
270 | if (i == 0) { | |
271 | r8712_write16(padapter, 0x10250348, 0xc000); | |
272 | r8712_write16(padapter, 0x10250348, 0xc001); | |
273 | r8712_write16(padapter, 0x10250348, 0x2000); | |
274 | r8712_write16(padapter, 0x10250348, 0x2001); | |
275 | r8712_write16(padapter, 0x10250348, 0x2002); | |
276 | r8712_write16(padapter, 0x10250348, 0x2003); | |
277 | goto exit_fail; | |
278 | } | |
279 | /* 5.Download DMEM code size and Load EMEM Code Section */ | |
280 | memset(ptx_desc, 0, TXDESC_SIZE); | |
335b9efa | 281 | ptx_desc->txdw0 |= cpu_to_le32(fwhdr.fw_priv_sz & 0x0000ffff); |
2865d42c LF |
282 | ptx_desc->txdw0 |= cpu_to_le32(BIT(28)); |
283 | memcpy(ppayload, &fwhdr.fwpriv, fwhdr.fw_priv_sz); | |
284 | r8712_write_mem(padapter, RTL8712_DMA_VOQ, | |
285 | fwhdr.fw_priv_sz + TXDESC_SIZE, (u8 *)ptx_desc); | |
286 | ||
287 | /* polling dmem code done */ | |
288 | i = 100; | |
289 | tmp16 = r8712_read16(padapter, TCR); | |
290 | while (((tmp16 & _DMEM_CODE_DONE) == 0) && (i > 0)) { | |
291 | msleep(20); | |
292 | tmp16 = r8712_read16(padapter, TCR); | |
293 | i--; | |
294 | } | |
295 | if (i == 0) | |
296 | goto exit_fail; | |
297 | ||
298 | tmp8 = r8712_read8(padapter, 0x1025000A); | |
299 | if (tmp8 & BIT(4)) /* When boot from EEPROM, | |
300 | & FW need more time to read EEPROM */ | |
301 | i = 60; | |
302 | else /* boot from EFUSE */ | |
303 | i = 30; | |
304 | tmp16 = r8712_read16(padapter, TCR); | |
305 | while (((tmp16 & _FWRDY) == 0) && (i > 0)) { | |
306 | msleep(100); | |
307 | tmp16 = r8712_read16(padapter, TCR); | |
308 | i--; | |
309 | } | |
310 | if (i == 0) | |
311 | goto exit_fail; | |
168a2c10 | 312 | } else { |
2865d42c | 313 | goto exit_fail; |
168a2c10 | 314 | } |
e375870b | 315 | ret = _SUCCESS; |
2865d42c LF |
316 | |
317 | exit_fail: | |
318 | kfree(ptmpchar); | |
e375870b | 319 | return ret; |
2865d42c LF |
320 | } |
321 | ||
322 | uint rtl8712_hal_init(struct _adapter *padapter) | |
323 | { | |
324 | u32 val32; | |
325 | int i; | |
326 | ||
327 | /* r8712 firmware download */ | |
328 | if (rtl8712_dl_fw(padapter) != _SUCCESS) | |
329 | return _FAIL; | |
330 | ||
87a573ad PF |
331 | netdev_info(padapter->pnetdev, "1 RCR=0x%x\n", |
332 | r8712_read32(padapter, RCR)); | |
2865d42c LF |
333 | val32 = r8712_read32(padapter, RCR); |
334 | r8712_write32(padapter, RCR, (val32 | BIT(26))); /* Enable RX TCP | |
335 | Checksum offload */ | |
87a573ad PF |
336 | netdev_info(padapter->pnetdev, "2 RCR=0x%x\n", |
337 | r8712_read32(padapter, RCR)); | |
2865d42c | 338 | val32 = r8712_read32(padapter, RCR); |
7ab4609a | 339 | r8712_write32(padapter, RCR, (val32 | BIT(25))); /* Append PHY status */ |
2865d42c LF |
340 | val32 = 0; |
341 | val32 = r8712_read32(padapter, 0x10250040); | |
335b9efa | 342 | r8712_write32(padapter, 0x10250040, (val32 & 0x00FFFFFF)); |
2865d42c LF |
343 | /* for usb rx aggregation */ |
344 | r8712_write8(padapter, 0x102500B5, r8712_read8(padapter, 0x102500B5) | | |
345 | BIT(0)); /* page = 128bytes */ | |
346 | r8712_write8(padapter, 0x102500BD, r8712_read8(padapter, 0x102500BD) | | |
347 | BIT(7)); /* enable usb rx aggregation */ | |
348 | r8712_write8(padapter, 0x102500D9, 1); /* TH=1 => means that invalidate | |
349 | * usb rx aggregation */ | |
350 | r8712_write8(padapter, 0x1025FE5B, 0x04); /* 1.7ms/4 */ | |
351 | /* Fix the RX FIFO issue(USB error) */ | |
352 | r8712_write8(padapter, 0x1025fe5C, r8712_read8(padapter, 0x1025fe5C) | |
353 | | BIT(7)); | |
354 | for (i = 0; i < 6; i++) | |
355 | padapter->eeprompriv.mac_addr[i] = r8712_read8(padapter, | |
356 | MACID + i); | |
357 | return _SUCCESS; | |
358 | } | |
359 | ||
360 | uint rtl8712_hal_deinit(struct _adapter *padapter) | |
361 | { | |
362 | r8712_write8(padapter, RF_CTRL, 0x00); | |
363 | /* Turn off BB */ | |
364 | msleep(20); | |
365 | /* Turn off MAC */ | |
b3845d02 AKC |
366 | r8712_write8(padapter, SYS_CLKR + 1, 0x38); /* Switch Control Path */ |
367 | r8712_write8(padapter, SYS_FUNC_EN + 1, 0x70); | |
2865d42c LF |
368 | r8712_write8(padapter, PMC_FSM, 0x06); /* Enable Loader Data Keep */ |
369 | r8712_write8(padapter, SYS_ISO_CTRL, 0xF9); /* Isolation signals from | |
370 | * CORE, PLL */ | |
b3845d02 | 371 | r8712_write8(padapter, SYS_ISO_CTRL + 1, 0xe8); /* Enable EFUSE 1.2V */ |
2865d42c LF |
372 | r8712_write8(padapter, AFE_PLL_CTRL, 0x00); /* Disable AFE PLL. */ |
373 | r8712_write8(padapter, LDOA15_CTRL, 0x54); /* Disable A15V */ | |
b3845d02 | 374 | r8712_write8(padapter, SYS_FUNC_EN + 1, 0x50); /* Disable E-Fuse 1.2V */ |
2865d42c LF |
375 | r8712_write8(padapter, LDOV12D_CTRL, 0x24); /* Disable LDO12(for CE) */ |
376 | r8712_write8(padapter, AFE_MISC, 0x30); /* Disable AFE BG&MB */ | |
377 | /* Option for Disable 1.6V LDO. */ | |
378 | r8712_write8(padapter, SPS0_CTRL, 0x56); /* Disable 1.6V LDO */ | |
b3845d02 | 379 | r8712_write8(padapter, SPS0_CTRL + 1, 0x43); /* Set SW PFM */ |
2865d42c LF |
380 | return _SUCCESS; |
381 | } | |
382 | ||
383 | uint rtl871x_hal_init(struct _adapter *padapter) | |
384 | { | |
385 | padapter->hw_init_completed = false; | |
8d2884ac | 386 | if (!padapter->halpriv.hal_bus_init) |
2865d42c | 387 | return _FAIL; |
bc394a39 SK |
388 | if (padapter->halpriv.hal_bus_init(padapter) != _SUCCESS) |
389 | return _FAIL; | |
168a2c10 | 390 | if (rtl8712_hal_init(padapter) == _SUCCESS) { |
2865d42c | 391 | padapter->hw_init_completed = true; |
168a2c10 | 392 | } else { |
2865d42c LF |
393 | padapter->hw_init_completed = false; |
394 | return _FAIL; | |
395 | } | |
396 | return _SUCCESS; | |
397 | } |