1 | /* |
2 | * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> |
3 | * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> |
4 | * |
5 | * Permission to use, copy, modify, and distribute this software for any |
6 | * purpose with or without fee is hereby granted, provided that the above |
7 | * copyright notice and this permission notice appear in all copies. |
8 | * |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 | * |
17 | */ |
18 | |
19 | /*************************************\ |
20 | * Attach/Detach Functions and helpers * |
21 | \*************************************/ |
22 | |
23 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
24 | |
25 | #include <linux/pci.h> |
26 | #include <linux/slab.h> |
27 | #include "ath5k.h" |
28 | #include "reg.h" |
29 | #include "debug.h" |
30 | |
31 | /** |
32 | * ath5k_hw_post() - Power On Self Test helper function |
33 | * @ah: The &struct ath5k_hw |
34 | */ |
35 | static int ath5k_hw_post(struct ath5k_hw *ah) |
36 | { |
37 | |
38 | static const u32 static_pattern[4] = { |
39 | 0x55555555, 0xaaaaaaaa, |
40 | 0x66666666, 0x99999999 |
41 | }; |
42 | static const u16 regs[2] = { AR5K_STA_ID0, AR5K_PHY(8) }; |
43 | int i, c; |
44 | u16 cur_reg; |
45 | u32 var_pattern; |
46 | u32 init_val; |
47 | u32 cur_val; |
48 | |
49 | for (c = 0; c < 2; c++) { |
50 | |
51 | cur_reg = regs[c]; |
52 | |
53 | /* Save previous value */ |
54 | init_val = ath5k_hw_reg_read(ah, reg: cur_reg); |
55 | |
56 | for (i = 0; i < 256; i++) { |
57 | var_pattern = i << 16 | i; |
58 | ath5k_hw_reg_write(ah, val: var_pattern, reg: cur_reg); |
59 | cur_val = ath5k_hw_reg_read(ah, reg: cur_reg); |
60 | |
61 | if (cur_val != var_pattern) { |
62 | ATH5K_ERR(ah, "POST Failed !!!\n" ); |
63 | return -EAGAIN; |
64 | } |
65 | |
66 | /* Found on ndiswrapper dumps */ |
67 | var_pattern = 0x0039080f; |
68 | ath5k_hw_reg_write(ah, val: var_pattern, reg: cur_reg); |
69 | } |
70 | |
71 | for (i = 0; i < 4; i++) { |
72 | var_pattern = static_pattern[i]; |
73 | ath5k_hw_reg_write(ah, val: var_pattern, reg: cur_reg); |
74 | cur_val = ath5k_hw_reg_read(ah, reg: cur_reg); |
75 | |
76 | if (cur_val != var_pattern) { |
77 | ATH5K_ERR(ah, "POST Failed !!!\n" ); |
78 | return -EAGAIN; |
79 | } |
80 | |
81 | /* Found on ndiswrapper dumps */ |
82 | var_pattern = 0x003b080f; |
83 | ath5k_hw_reg_write(ah, val: var_pattern, reg: cur_reg); |
84 | } |
85 | |
86 | /* Restore previous value */ |
87 | ath5k_hw_reg_write(ah, val: init_val, reg: cur_reg); |
88 | |
89 | } |
90 | |
91 | return 0; |
92 | |
93 | } |
94 | |
95 | /** |
96 | * ath5k_hw_init() - Check if hw is supported and init the needed structs |
97 | * @ah: The &struct ath5k_hw associated with the device |
98 | * |
99 | * Check if the device is supported, perform a POST and initialize the needed |
100 | * structs. Returns -ENOMEM if we don't have memory for the needed structs, |
101 | * -ENODEV if the device is not supported or prints an error msg if something |
102 | * else went wrong. |
103 | */ |
104 | int ath5k_hw_init(struct ath5k_hw *ah) |
105 | { |
106 | static const u8 zero_mac[ETH_ALEN] = { }; |
107 | struct ath_common *common = ath5k_hw_common(ah); |
108 | struct pci_dev *pdev = ah->pdev; |
109 | struct ath5k_eeprom_info *ee; |
110 | int ret; |
111 | u32 srev; |
112 | |
113 | /* |
114 | * HW information |
115 | */ |
116 | ah->ah_bwmode = AR5K_BWMODE_DEFAULT; |
117 | ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER; |
118 | ah->ah_imr = 0; |
119 | ah->ah_retry_short = AR5K_INIT_RETRY_SHORT; |
120 | ah->ah_retry_long = AR5K_INIT_RETRY_LONG; |
121 | ah->ah_ant_mode = AR5K_ANTMODE_DEFAULT; |
122 | ah->ah_noise_floor = -95; /* until first NF calibration is run */ |
123 | ah->ani_state.ani_mode = ATH5K_ANI_MODE_AUTO; |
124 | ah->ah_current_channel = &ah->channels[0]; |
125 | |
126 | /* |
127 | * Find the mac version |
128 | */ |
129 | ath5k_hw_read_srev(ah); |
130 | srev = ah->ah_mac_srev; |
131 | if (srev < AR5K_SREV_AR5311) |
132 | ah->ah_version = AR5K_AR5210; |
133 | else if (srev < AR5K_SREV_AR5212) |
134 | ah->ah_version = AR5K_AR5211; |
135 | else |
136 | ah->ah_version = AR5K_AR5212; |
137 | |
138 | /* Get the MAC version */ |
139 | ah->ah_mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER); |
140 | |
141 | /* Fill the ath5k_hw struct with the needed functions */ |
142 | ret = ath5k_hw_init_desc_functions(ah); |
143 | if (ret) |
144 | goto err; |
145 | |
146 | /* Bring device out of sleep and reset its units */ |
147 | ret = ath5k_hw_nic_wakeup(ah, NULL); |
148 | if (ret) |
149 | goto err; |
150 | |
151 | /* Get PHY and RADIO revisions */ |
152 | ah->ah_phy_revision = ath5k_hw_reg_read(ah, AR5K_PHY_CHIP_ID) & |
153 | 0xffffffff; |
154 | ah->ah_radio_5ghz_revision = ath5k_hw_radio_revision(ah, |
155 | band: NL80211_BAND_5GHZ); |
156 | |
157 | /* Try to identify radio chip based on its srev */ |
158 | switch (ah->ah_radio_5ghz_revision & 0xf0) { |
159 | case AR5K_SREV_RAD_5111: |
160 | ah->ah_radio = AR5K_RF5111; |
161 | ah->ah_single_chip = false; |
162 | ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah, |
163 | band: NL80211_BAND_2GHZ); |
164 | break; |
165 | case AR5K_SREV_RAD_5112: |
166 | case AR5K_SREV_RAD_2112: |
167 | ah->ah_radio = AR5K_RF5112; |
168 | ah->ah_single_chip = false; |
169 | ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah, |
170 | band: NL80211_BAND_2GHZ); |
171 | break; |
172 | case AR5K_SREV_RAD_2413: |
173 | ah->ah_radio = AR5K_RF2413; |
174 | ah->ah_single_chip = true; |
175 | break; |
176 | case AR5K_SREV_RAD_5413: |
177 | ah->ah_radio = AR5K_RF5413; |
178 | ah->ah_single_chip = true; |
179 | break; |
180 | case AR5K_SREV_RAD_2316: |
181 | ah->ah_radio = AR5K_RF2316; |
182 | ah->ah_single_chip = true; |
183 | break; |
184 | case AR5K_SREV_RAD_2317: |
185 | ah->ah_radio = AR5K_RF2317; |
186 | ah->ah_single_chip = true; |
187 | break; |
188 | case AR5K_SREV_RAD_5424: |
189 | if (ah->ah_mac_version == AR5K_SREV_AR2425 || |
190 | ah->ah_mac_version == AR5K_SREV_AR2417) { |
191 | ah->ah_radio = AR5K_RF2425; |
192 | ah->ah_single_chip = true; |
193 | } else { |
194 | ah->ah_radio = AR5K_RF5413; |
195 | ah->ah_single_chip = true; |
196 | } |
197 | break; |
198 | default: |
199 | /* Identify radio based on mac/phy srev */ |
200 | if (ah->ah_version == AR5K_AR5210) { |
201 | ah->ah_radio = AR5K_RF5110; |
202 | ah->ah_single_chip = false; |
203 | } else if (ah->ah_version == AR5K_AR5211) { |
204 | ah->ah_radio = AR5K_RF5111; |
205 | ah->ah_single_chip = false; |
206 | ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah, |
207 | band: NL80211_BAND_2GHZ); |
208 | } else if (ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4) || |
209 | ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4) || |
210 | ah->ah_phy_revision == AR5K_SREV_PHY_2425) { |
211 | ah->ah_radio = AR5K_RF2425; |
212 | ah->ah_single_chip = true; |
213 | ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2425; |
214 | } else if (srev == AR5K_SREV_AR5213A && |
215 | ah->ah_phy_revision == AR5K_SREV_PHY_5212B) { |
216 | ah->ah_radio = AR5K_RF5112; |
217 | ah->ah_single_chip = false; |
218 | ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_5112B; |
219 | } else if (ah->ah_mac_version == (AR5K_SREV_AR2415 >> 4) || |
220 | ah->ah_mac_version == (AR5K_SREV_AR2315_R6 >> 4)) { |
221 | ah->ah_radio = AR5K_RF2316; |
222 | ah->ah_single_chip = true; |
223 | ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2316; |
224 | } else if (ah->ah_mac_version == (AR5K_SREV_AR5414 >> 4) || |
225 | ah->ah_phy_revision == AR5K_SREV_PHY_5413) { |
226 | ah->ah_radio = AR5K_RF5413; |
227 | ah->ah_single_chip = true; |
228 | ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_5413; |
229 | } else if (ah->ah_mac_version == (AR5K_SREV_AR2414 >> 4) || |
230 | ah->ah_phy_revision == AR5K_SREV_PHY_2413) { |
231 | ah->ah_radio = AR5K_RF2413; |
232 | ah->ah_single_chip = true; |
233 | ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2413; |
234 | } else { |
235 | ATH5K_ERR(ah, "Couldn't identify radio revision.\n" ); |
236 | ret = -ENODEV; |
237 | goto err; |
238 | } |
239 | } |
240 | |
241 | |
242 | /* Return on unsupported chips (unsupported eeprom etc) */ |
243 | if ((srev >= AR5K_SREV_AR5416) && (srev < AR5K_SREV_AR2425)) { |
244 | ATH5K_ERR(ah, "Device not yet supported.\n" ); |
245 | ret = -ENODEV; |
246 | goto err; |
247 | } |
248 | |
249 | /* |
250 | * POST |
251 | */ |
252 | ret = ath5k_hw_post(ah); |
253 | if (ret) |
254 | goto err; |
255 | |
256 | /* Enable pci core retry fix on Hainan (5213A) and later chips */ |
257 | if (srev >= AR5K_SREV_AR5213A) |
258 | AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_RETRY_FIX); |
259 | |
260 | /* |
261 | * Get card capabilities, calibration values etc |
262 | * TODO: EEPROM work |
263 | */ |
264 | ret = ath5k_eeprom_init(ah); |
265 | if (ret) { |
266 | ATH5K_ERR(ah, "unable to init EEPROM\n" ); |
267 | goto err; |
268 | } |
269 | |
270 | ee = &ah->ah_capabilities.cap_eeprom; |
271 | |
272 | /* |
273 | * Write PCI-E power save settings |
274 | */ |
275 | if ((ah->ah_version == AR5K_AR5212) && pdev && (pci_is_pcie(dev: pdev))) { |
276 | ath5k_hw_reg_write(ah, val: 0x9248fc00, AR5K_PCIE_SERDES); |
277 | ath5k_hw_reg_write(ah, val: 0x24924924, AR5K_PCIE_SERDES); |
278 | |
279 | /* Shut off RX when elecidle is asserted */ |
280 | ath5k_hw_reg_write(ah, val: 0x28000039, AR5K_PCIE_SERDES); |
281 | ath5k_hw_reg_write(ah, val: 0x53160824, AR5K_PCIE_SERDES); |
282 | |
283 | /* If serdes programming is enabled, increase PCI-E |
284 | * tx power for systems with long trace from host |
285 | * to minicard connector. */ |
286 | if (ee->ee_serdes) |
287 | ath5k_hw_reg_write(ah, val: 0xe5980579, AR5K_PCIE_SERDES); |
288 | else |
289 | ath5k_hw_reg_write(ah, val: 0xf6800579, AR5K_PCIE_SERDES); |
290 | |
291 | /* Shut off PLL and CLKREQ active in L1 */ |
292 | ath5k_hw_reg_write(ah, val: 0x001defff, AR5K_PCIE_SERDES); |
293 | |
294 | /* Preserve other settings */ |
295 | ath5k_hw_reg_write(ah, val: 0x1aaabe40, AR5K_PCIE_SERDES); |
296 | ath5k_hw_reg_write(ah, val: 0xbe105554, AR5K_PCIE_SERDES); |
297 | ath5k_hw_reg_write(ah, val: 0x000e3007, AR5K_PCIE_SERDES); |
298 | |
299 | /* Reset SERDES to load new settings */ |
300 | ath5k_hw_reg_write(ah, val: 0x00000000, AR5K_PCIE_SERDES_RESET); |
301 | usleep_range(min: 1000, max: 1500); |
302 | } |
303 | |
304 | /* Get misc capabilities */ |
305 | ret = ath5k_hw_set_capabilities(ah); |
306 | if (ret) { |
307 | ATH5K_ERR(ah, "unable to get device capabilities\n" ); |
308 | goto err; |
309 | } |
310 | |
311 | /* Crypto settings */ |
312 | common->keymax = (ah->ah_version == AR5K_AR5210 ? |
313 | AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211); |
314 | |
315 | if (srev >= AR5K_SREV_AR5212_V4 && |
316 | (ee->ee_version < AR5K_EEPROM_VERSION_5_0 || |
317 | !AR5K_EEPROM_AES_DIS(ee->ee_misc5))) |
318 | common->crypt_caps |= ATH_CRYPT_CAP_CIPHER_AESCCM; |
319 | |
320 | if (srev >= AR5K_SREV_AR2414) { |
321 | common->crypt_caps |= ATH_CRYPT_CAP_MIC_COMBINED; |
322 | AR5K_REG_ENABLE_BITS(ah, AR5K_MISC_MODE, |
323 | AR5K_MISC_MODE_COMBINED_MIC); |
324 | } |
325 | |
326 | /* MAC address is cleared until add_interface */ |
327 | ath5k_hw_set_lladdr(ah, mac: zero_mac); |
328 | |
329 | /* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */ |
330 | eth_broadcast_addr(addr: common->curbssid); |
331 | ath5k_hw_set_bssid(ah); |
332 | ath5k_hw_set_opmode(ah, opmode: ah->opmode); |
333 | |
334 | ath5k_hw_rfgain_opt_init(ah); |
335 | |
336 | ath5k_hw_init_nfcal_hist(ah); |
337 | |
338 | /* turn on HW LEDs */ |
339 | ath5k_hw_set_ledstate(ah, AR5K_LED_INIT); |
340 | |
341 | return 0; |
342 | err: |
343 | return ret; |
344 | } |
345 | |
346 | /** |
347 | * ath5k_hw_deinit() - Free the &struct ath5k_hw |
348 | * @ah: The &struct ath5k_hw |
349 | */ |
350 | void ath5k_hw_deinit(struct ath5k_hw *ah) |
351 | { |
352 | __set_bit(ATH_STAT_INVALID, ah->status); |
353 | |
354 | kfree(objp: ah->ah_rf_banks); |
355 | |
356 | ath5k_eeprom_detach(ah); |
357 | |
358 | /* assume interrupts are down */ |
359 | } |
360 | |