Larry Finger | 2865d42 | 2010-08-20 10:15:30 -0500 | [diff] [blame] | 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 | |
| 31 | #include "osdep_service.h" |
| 32 | #include "drv_types.h" |
| 33 | #include "rtl871x_byteorder.h" |
| 34 | #include "farray.h" |
Larry Finger | 16e5372 | 2010-08-30 20:43:44 -0500 | [diff] [blame] | 35 | #include "usb_osintf.h" |
Larry Finger | 2865d42 | 2010-08-20 10:15:30 -0500 | [diff] [blame] | 36 | |
| 37 | #define FWBUFF_ALIGN_SZ 512 |
| 38 | #define MAX_DUMP_FWSZ 49152 /*default = 49152 (48k)*/ |
| 39 | |
| 40 | static u32 rtl871x_open_fw(struct _adapter *padapter, void **pphfwfile_hdl, |
| 41 | const u8 **ppmappedfw) |
| 42 | { |
| 43 | u32 len; |
| 44 | |
| 45 | *ppmappedfw = f_array; |
| 46 | len = sizeof(f_array); |
| 47 | return len; |
| 48 | } |
| 49 | |
| 50 | static void fill_fwpriv(struct _adapter *padapter, struct fw_priv *pfwpriv) |
| 51 | { |
| 52 | struct dvobj_priv *pdvobj = (struct dvobj_priv *)&padapter->dvobjpriv; |
| 53 | struct registry_priv *pregpriv = &padapter->registrypriv; |
| 54 | |
| 55 | memset(pfwpriv, 0, sizeof(struct fw_priv)); |
| 56 | /* todo: check if needs endian conversion */ |
| 57 | pfwpriv->hci_sel = RTL8712_HCI_TYPE_72USB; |
| 58 | pfwpriv->usb_ep_num = (u8)pdvobj->nr_endpoint; |
| 59 | pfwpriv->bw_40MHz_en = pregpriv->cbw40_enable; |
| 60 | switch (pregpriv->rf_config) { |
| 61 | case RTL8712_RF_1T1R: |
| 62 | pfwpriv->rf_config = RTL8712_RFC_1T1R; |
| 63 | break; |
| 64 | case RTL8712_RF_2T2R: |
| 65 | pfwpriv->rf_config = RTL8712_RFC_2T2R; |
| 66 | break; |
| 67 | case RTL8712_RF_1T2R: |
| 68 | default: |
| 69 | pfwpriv->rf_config = RTL8712_RFC_1T2R; |
| 70 | } |
| 71 | pfwpriv->mp_mode = (pregpriv->mp_mode == 1) ? 1 : 0; |
| 72 | pfwpriv->vcsType = pregpriv->vrtl_carrier_sense; /* 0:off 1:on 2:auto */ |
| 73 | pfwpriv->vcsMode = pregpriv->vcs_type; /* 1:RTS/CTS 2:CTS to self */ |
Larry Finger | d19b864 | 2010-08-30 22:51:58 -0500 | [diff] [blame^] | 74 | /* default enable turboMode */ |
| 75 | pfwpriv->turboMode = ((pregpriv->wifi_test == 1) ? 0 : 1); |
Larry Finger | 2865d42 | 2010-08-20 10:15:30 -0500 | [diff] [blame] | 76 | pfwpriv->lowPowerMode = pregpriv->low_power; |
| 77 | } |
| 78 | |
| 79 | static void update_fwhdr(struct fw_hdr *pfwhdr, const u8 *pmappedfw) |
| 80 | { |
| 81 | pfwhdr->signature = le16_to_cpu(*(u16 *)pmappedfw); |
| 82 | pfwhdr->version = le16_to_cpu(*(u16 *)(pmappedfw+2)); |
| 83 | /* define the size of boot loader */ |
| 84 | pfwhdr->dmem_size = le32_to_cpu(*(uint *)(pmappedfw+4)); |
| 85 | /* define the size of FW in IMEM */ |
| 86 | pfwhdr->img_IMEM_size = le32_to_cpu(*(uint *)(pmappedfw+8)); |
| 87 | /* define the size of FW in SRAM */ |
| 88 | pfwhdr->img_SRAM_size = le32_to_cpu(*(uint *)(pmappedfw+12)); |
| 89 | /* define the size of DMEM variable */ |
| 90 | pfwhdr->fw_priv_sz = le32_to_cpu(*(uint *)(pmappedfw+16)); |
| 91 | } |
| 92 | |
| 93 | static u8 chk_fwhdr(struct fw_hdr *pfwhdr, u32 ulfilelength) |
| 94 | { |
| 95 | u32 fwhdrsz, fw_sz; |
| 96 | u8 intf, rfconf; |
| 97 | |
| 98 | /* check signature */ |
| 99 | if ((pfwhdr->signature != 0x8712) && (pfwhdr->signature != 0x8192)) |
| 100 | return _FAIL; |
| 101 | /* check interface */ |
| 102 | intf = (u8)((pfwhdr->version&0x3000) >> 12); |
| 103 | /* check rf_conf */ |
| 104 | rfconf = (u8)((pfwhdr->version&0xC000) >> 14); |
| 105 | /* check fw_priv_sze & sizeof(struct fw_priv) */ |
| 106 | if (pfwhdr->fw_priv_sz != sizeof(struct fw_priv)) |
| 107 | return _FAIL; |
| 108 | /* check fw_sz & image_fw_sz */ |
| 109 | fwhdrsz = FIELD_OFFSET(struct fw_hdr, fwpriv) + pfwhdr->fw_priv_sz; |
| 110 | fw_sz = fwhdrsz + pfwhdr->img_IMEM_size + pfwhdr->img_SRAM_size + |
| 111 | pfwhdr->dmem_size; |
| 112 | if (fw_sz != ulfilelength) |
| 113 | return _FAIL; |
| 114 | return _SUCCESS; |
| 115 | } |
| 116 | |
| 117 | static u8 rtl8712_dl_fw(struct _adapter *padapter) |
| 118 | { |
| 119 | sint i; |
| 120 | u8 tmp8, tmp8_a; |
| 121 | u16 tmp16; |
| 122 | u32 maxlen = 0, tmp32; /* for compare usage */ |
| 123 | uint dump_imem_sz, imem_sz, dump_emem_sz, emem_sz; /* max = 49152; */ |
| 124 | struct fw_hdr fwhdr; |
| 125 | u32 ulfilelength; /* FW file size */ |
| 126 | void *phfwfile_hdl = NULL; |
| 127 | const u8 *pmappedfw = NULL; |
| 128 | u8 *ptmpchar = NULL, *ppayload, *ptr; |
| 129 | struct tx_desc *ptx_desc; |
| 130 | u32 txdscp_sz = sizeof(struct tx_desc); |
| 131 | |
| 132 | ulfilelength = rtl871x_open_fw(padapter, &phfwfile_hdl, &pmappedfw); |
| 133 | if (pmappedfw && (ulfilelength > 0)) { |
| 134 | update_fwhdr(&fwhdr, pmappedfw); |
| 135 | if (chk_fwhdr(&fwhdr, ulfilelength) == _FAIL) |
| 136 | goto exit_fail; |
| 137 | fill_fwpriv(padapter, &fwhdr.fwpriv); |
| 138 | /* firmware check ok */ |
| 139 | maxlen = (fwhdr.img_IMEM_size > fwhdr.img_SRAM_size) ? |
| 140 | fwhdr.img_IMEM_size : fwhdr.img_SRAM_size; |
| 141 | maxlen += txdscp_sz; |
| 142 | ptmpchar = _malloc(maxlen + FWBUFF_ALIGN_SZ); |
| 143 | if (ptmpchar == NULL) |
| 144 | return _FAIL; |
| 145 | |
| 146 | ptx_desc = (struct tx_desc *)(ptmpchar + FWBUFF_ALIGN_SZ - |
| 147 | ((addr_t)(ptmpchar) & (FWBUFF_ALIGN_SZ - 1))); |
| 148 | ppayload = (u8 *)(ptx_desc) + txdscp_sz; |
| 149 | ptr = (u8 *)pmappedfw + FIELD_OFFSET(struct fw_hdr, fwpriv) + |
| 150 | fwhdr.fw_priv_sz; |
| 151 | /* Download FirmWare */ |
| 152 | /* 1. determine IMEM code size and Load IMEM Code Section */ |
| 153 | imem_sz = fwhdr.img_IMEM_size; |
| 154 | do { |
| 155 | memset(ptx_desc, 0, TXDESC_SIZE); |
| 156 | if (imem_sz > MAX_DUMP_FWSZ/*49152*/) |
| 157 | dump_imem_sz = MAX_DUMP_FWSZ; |
| 158 | else { |
| 159 | dump_imem_sz = imem_sz; |
| 160 | ptx_desc->txdw0 |= cpu_to_le32(BIT(28)); |
| 161 | } |
| 162 | ptx_desc->txdw0 |= cpu_to_le32(dump_imem_sz & |
| 163 | 0x0000ffff); |
| 164 | memcpy(ppayload, ptr, dump_imem_sz); |
| 165 | r8712_write_mem(padapter, RTL8712_DMA_VOQ, |
| 166 | dump_imem_sz + TXDESC_SIZE, |
| 167 | (u8 *)ptx_desc); |
| 168 | ptr += dump_imem_sz; |
| 169 | imem_sz -= dump_imem_sz; |
| 170 | } while (imem_sz > 0); |
| 171 | i = 10; |
| 172 | tmp16 = r8712_read16(padapter, TCR); |
| 173 | while (((tmp16 & _IMEM_CODE_DONE) == 0) && (i > 0)) { |
| 174 | udelay(10); |
| 175 | tmp16 = r8712_read16(padapter, TCR); |
| 176 | i--; |
| 177 | } |
| 178 | if (i == 0 || (tmp16 & _IMEM_CHK_RPT) == 0) |
| 179 | goto exit_fail; |
| 180 | |
| 181 | /* 2.Download EMEM code size and Load EMEM Code Section */ |
| 182 | emem_sz = fwhdr.img_SRAM_size; |
| 183 | do { |
| 184 | memset(ptx_desc, 0, TXDESC_SIZE); |
| 185 | if (emem_sz > MAX_DUMP_FWSZ) /* max=48k */ |
| 186 | dump_emem_sz = MAX_DUMP_FWSZ; |
| 187 | else { |
| 188 | dump_emem_sz = emem_sz; |
| 189 | ptx_desc->txdw0 |= cpu_to_le32(BIT(28)); |
| 190 | } |
| 191 | ptx_desc->txdw0 |= cpu_to_le32(dump_emem_sz & |
| 192 | 0x0000ffff); |
| 193 | memcpy(ppayload, ptr, dump_emem_sz); |
| 194 | r8712_write_mem(padapter, RTL8712_DMA_VOQ, |
| 195 | dump_emem_sz+TXDESC_SIZE, (u8 *)ptx_desc); |
| 196 | ptr += dump_emem_sz; |
| 197 | emem_sz -= dump_emem_sz; |
| 198 | } while (emem_sz > 0); |
| 199 | i = 5; |
| 200 | tmp16 = r8712_read16(padapter, TCR); |
| 201 | while (((tmp16 & _EMEM_CODE_DONE) == 0) && (i > 0)) { |
| 202 | udelay(10); |
| 203 | tmp16 = r8712_read16(padapter, TCR); |
| 204 | i--; |
| 205 | } |
| 206 | if (i == 0 || (tmp16 & _EMEM_CHK_RPT) == 0) |
| 207 | goto exit_fail; |
| 208 | |
| 209 | /* 3.Enable CPU */ |
| 210 | tmp8 = r8712_read8(padapter, SYS_CLKR); |
| 211 | r8712_write8(padapter, SYS_CLKR, tmp8|BIT(2)); |
| 212 | tmp8_a = r8712_read8(padapter, SYS_CLKR); |
| 213 | if (tmp8_a != (tmp8|BIT(2))) |
| 214 | goto exit_fail; |
| 215 | |
| 216 | tmp8 = r8712_read8(padapter, SYS_FUNC_EN + 1); |
| 217 | r8712_write8(padapter, SYS_FUNC_EN+1, tmp8|BIT(2)); |
| 218 | tmp8_a = r8712_read8(padapter, SYS_FUNC_EN + 1); |
| 219 | if (tmp8_a != (tmp8|BIT(2))) |
| 220 | goto exit_fail; |
| 221 | |
| 222 | tmp32 = r8712_read32(padapter, TCR); |
| 223 | |
| 224 | /* 4.polling IMEM Ready */ |
| 225 | i = 100; |
| 226 | tmp16 = r8712_read16(padapter, TCR); |
| 227 | while (((tmp16 & _IMEM_RDY) == 0) && (i > 0)) { |
| 228 | msleep(20); |
| 229 | tmp16 = r8712_read16(padapter, TCR); |
| 230 | i--; |
| 231 | } |
| 232 | if (i == 0) { |
| 233 | r8712_write16(padapter, 0x10250348, 0xc000); |
| 234 | r8712_write16(padapter, 0x10250348, 0xc001); |
| 235 | r8712_write16(padapter, 0x10250348, 0x2000); |
| 236 | r8712_write16(padapter, 0x10250348, 0x2001); |
| 237 | r8712_write16(padapter, 0x10250348, 0x2002); |
| 238 | r8712_write16(padapter, 0x10250348, 0x2003); |
| 239 | goto exit_fail; |
| 240 | } |
| 241 | /* 5.Download DMEM code size and Load EMEM Code Section */ |
| 242 | memset(ptx_desc, 0, TXDESC_SIZE); |
| 243 | ptx_desc->txdw0 |= cpu_to_le32(fwhdr.fw_priv_sz&0x0000ffff); |
| 244 | ptx_desc->txdw0 |= cpu_to_le32(BIT(28)); |
| 245 | memcpy(ppayload, &fwhdr.fwpriv, fwhdr.fw_priv_sz); |
| 246 | r8712_write_mem(padapter, RTL8712_DMA_VOQ, |
| 247 | fwhdr.fw_priv_sz + TXDESC_SIZE, (u8 *)ptx_desc); |
| 248 | |
| 249 | /* polling dmem code done */ |
| 250 | i = 100; |
| 251 | tmp16 = r8712_read16(padapter, TCR); |
| 252 | while (((tmp16 & _DMEM_CODE_DONE) == 0) && (i > 0)) { |
| 253 | msleep(20); |
| 254 | tmp16 = r8712_read16(padapter, TCR); |
| 255 | i--; |
| 256 | } |
| 257 | if (i == 0) |
| 258 | goto exit_fail; |
| 259 | |
| 260 | tmp8 = r8712_read8(padapter, 0x1025000A); |
| 261 | if (tmp8 & BIT(4)) /* When boot from EEPROM, |
| 262 | & FW need more time to read EEPROM */ |
| 263 | i = 60; |
| 264 | else /* boot from EFUSE */ |
| 265 | i = 30; |
| 266 | tmp16 = r8712_read16(padapter, TCR); |
| 267 | while (((tmp16 & _FWRDY) == 0) && (i > 0)) { |
| 268 | msleep(100); |
| 269 | tmp16 = r8712_read16(padapter, TCR); |
| 270 | i--; |
| 271 | } |
| 272 | if (i == 0) |
| 273 | goto exit_fail; |
| 274 | } else |
| 275 | goto exit_fail; |
| 276 | return _SUCCESS; |
| 277 | |
| 278 | exit_fail: |
| 279 | kfree(ptmpchar); |
| 280 | return _FAIL; |
| 281 | } |
| 282 | |
| 283 | uint rtl8712_hal_init(struct _adapter *padapter) |
| 284 | { |
| 285 | u32 val32; |
| 286 | int i; |
| 287 | |
| 288 | /* r8712 firmware download */ |
| 289 | if (rtl8712_dl_fw(padapter) != _SUCCESS) |
| 290 | return _FAIL; |
| 291 | |
| 292 | printk(KERN_INFO "r8712u: 1 RCR=0x%x\n", r8712_read32(padapter, RCR)); |
| 293 | val32 = r8712_read32(padapter, RCR); |
| 294 | r8712_write32(padapter, RCR, (val32 | BIT(26))); /* Enable RX TCP |
| 295 | Checksum offload */ |
| 296 | printk(KERN_INFO "r8712u: 2 RCR=0x%x\n", r8712_read32(padapter, RCR)); |
| 297 | val32 = r8712_read32(padapter, RCR); |
| 298 | r8712_write32(padapter, RCR, (val32|BIT(25))); /* Append PHY status */ |
| 299 | val32 = 0; |
| 300 | val32 = r8712_read32(padapter, 0x10250040); |
| 301 | r8712_write32(padapter, 0x10250040, (val32&0x00FFFFFF)); |
| 302 | /* for usb rx aggregation */ |
| 303 | r8712_write8(padapter, 0x102500B5, r8712_read8(padapter, 0x102500B5) | |
| 304 | BIT(0)); /* page = 128bytes */ |
| 305 | r8712_write8(padapter, 0x102500BD, r8712_read8(padapter, 0x102500BD) | |
| 306 | BIT(7)); /* enable usb rx aggregation */ |
| 307 | r8712_write8(padapter, 0x102500D9, 1); /* TH=1 => means that invalidate |
| 308 | * usb rx aggregation */ |
| 309 | r8712_write8(padapter, 0x1025FE5B, 0x04); /* 1.7ms/4 */ |
| 310 | /* Fix the RX FIFO issue(USB error) */ |
| 311 | r8712_write8(padapter, 0x1025fe5C, r8712_read8(padapter, 0x1025fe5C) |
| 312 | | BIT(7)); |
| 313 | for (i = 0; i < 6; i++) |
| 314 | padapter->eeprompriv.mac_addr[i] = r8712_read8(padapter, |
| 315 | MACID + i); |
| 316 | return _SUCCESS; |
| 317 | } |
| 318 | |
| 319 | uint rtl8712_hal_deinit(struct _adapter *padapter) |
| 320 | { |
| 321 | r8712_write8(padapter, RF_CTRL, 0x00); |
| 322 | /* Turn off BB */ |
| 323 | msleep(20); |
| 324 | /* Turn off MAC */ |
| 325 | r8712_write8(padapter, SYS_CLKR+1, 0x38); /* Switch Control Path */ |
| 326 | r8712_write8(padapter, SYS_FUNC_EN+1, 0x70); |
| 327 | r8712_write8(padapter, PMC_FSM, 0x06); /* Enable Loader Data Keep */ |
| 328 | r8712_write8(padapter, SYS_ISO_CTRL, 0xF9); /* Isolation signals from |
| 329 | * CORE, PLL */ |
| 330 | r8712_write8(padapter, SYS_ISO_CTRL+1, 0xe8); /* Enable EFUSE 1.2V */ |
| 331 | r8712_write8(padapter, AFE_PLL_CTRL, 0x00); /* Disable AFE PLL. */ |
| 332 | r8712_write8(padapter, LDOA15_CTRL, 0x54); /* Disable A15V */ |
| 333 | r8712_write8(padapter, SYS_FUNC_EN+1, 0x50); /* Disable E-Fuse 1.2V */ |
| 334 | r8712_write8(padapter, LDOV12D_CTRL, 0x24); /* Disable LDO12(for CE) */ |
| 335 | r8712_write8(padapter, AFE_MISC, 0x30); /* Disable AFE BG&MB */ |
| 336 | /* Option for Disable 1.6V LDO. */ |
| 337 | r8712_write8(padapter, SPS0_CTRL, 0x56); /* Disable 1.6V LDO */ |
| 338 | r8712_write8(padapter, SPS0_CTRL+1, 0x43); /* Set SW PFM */ |
| 339 | return _SUCCESS; |
| 340 | } |
| 341 | |
| 342 | uint rtl871x_hal_init(struct _adapter *padapter) |
| 343 | { |
| 344 | padapter->hw_init_completed = false; |
| 345 | if (padapter->halpriv.hal_bus_init == NULL) |
| 346 | return _FAIL; |
| 347 | else { |
| 348 | if (padapter->halpriv.hal_bus_init(padapter) != _SUCCESS) |
| 349 | return _FAIL; |
| 350 | } |
| 351 | if (rtl8712_hal_init(padapter) == _SUCCESS) |
| 352 | padapter->hw_init_completed = true; |
| 353 | else { |
| 354 | padapter->hw_init_completed = false; |
| 355 | return _FAIL; |
| 356 | } |
| 357 | return _SUCCESS; |
| 358 | } |