diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/phy.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/phy.c | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath9k/phy.c b/drivers/net/wireless/ath/ath9k/phy.c new file mode 100644 index 000000000000..5ec9ce91d979 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/phy.c | |||
@@ -0,0 +1,424 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008-2009 Atheros Communications Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include "ath9k.h" | ||
18 | |||
19 | void | ||
20 | ath9k_hw_write_regs(struct ath_hw *ah, u32 modesIndex, u32 freqIndex, | ||
21 | int regWrites) | ||
22 | { | ||
23 | REG_WRITE_ARRAY(&ah->iniBB_RfGain, freqIndex, regWrites); | ||
24 | } | ||
25 | |||
26 | bool | ||
27 | ath9k_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) | ||
28 | { | ||
29 | u32 channelSel = 0; | ||
30 | u32 bModeSynth = 0; | ||
31 | u32 aModeRefSel = 0; | ||
32 | u32 reg32 = 0; | ||
33 | u16 freq; | ||
34 | struct chan_centers centers; | ||
35 | |||
36 | ath9k_hw_get_channel_centers(ah, chan, ¢ers); | ||
37 | freq = centers.synth_center; | ||
38 | |||
39 | if (freq < 4800) { | ||
40 | u32 txctl; | ||
41 | |||
42 | if (((freq - 2192) % 5) == 0) { | ||
43 | channelSel = ((freq - 672) * 2 - 3040) / 10; | ||
44 | bModeSynth = 0; | ||
45 | } else if (((freq - 2224) % 5) == 0) { | ||
46 | channelSel = ((freq - 704) * 2 - 3040) / 10; | ||
47 | bModeSynth = 1; | ||
48 | } else { | ||
49 | DPRINTF(ah->ah_sc, ATH_DBG_FATAL, | ||
50 | "Invalid channel %u MHz\n", freq); | ||
51 | return false; | ||
52 | } | ||
53 | |||
54 | channelSel = (channelSel << 2) & 0xff; | ||
55 | channelSel = ath9k_hw_reverse_bits(channelSel, 8); | ||
56 | |||
57 | txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL); | ||
58 | if (freq == 2484) { | ||
59 | |||
60 | REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, | ||
61 | txctl | AR_PHY_CCK_TX_CTRL_JAPAN); | ||
62 | } else { | ||
63 | REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, | ||
64 | txctl & ~AR_PHY_CCK_TX_CTRL_JAPAN); | ||
65 | } | ||
66 | |||
67 | } else if ((freq % 20) == 0 && freq >= 5120) { | ||
68 | channelSel = | ||
69 | ath9k_hw_reverse_bits(((freq - 4800) / 20 << 2), 8); | ||
70 | aModeRefSel = ath9k_hw_reverse_bits(1, 2); | ||
71 | } else if ((freq % 10) == 0) { | ||
72 | channelSel = | ||
73 | ath9k_hw_reverse_bits(((freq - 4800) / 10 << 1), 8); | ||
74 | if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) | ||
75 | aModeRefSel = ath9k_hw_reverse_bits(2, 2); | ||
76 | else | ||
77 | aModeRefSel = ath9k_hw_reverse_bits(1, 2); | ||
78 | } else if ((freq % 5) == 0) { | ||
79 | channelSel = ath9k_hw_reverse_bits((freq - 4800) / 5, 8); | ||
80 | aModeRefSel = ath9k_hw_reverse_bits(1, 2); | ||
81 | } else { | ||
82 | DPRINTF(ah->ah_sc, ATH_DBG_FATAL, | ||
83 | "Invalid channel %u MHz\n", freq); | ||
84 | return false; | ||
85 | } | ||
86 | |||
87 | reg32 = | ||
88 | (channelSel << 8) | (aModeRefSel << 2) | (bModeSynth << 1) | | ||
89 | (1 << 5) | 0x1; | ||
90 | |||
91 | REG_WRITE(ah, AR_PHY(0x37), reg32); | ||
92 | |||
93 | ah->curchan = chan; | ||
94 | ah->curchan_rad_index = -1; | ||
95 | |||
96 | return true; | ||
97 | } | ||
98 | |||
99 | bool | ||
100 | ath9k_hw_ar9280_set_channel(struct ath_hw *ah, | ||
101 | struct ath9k_channel *chan) | ||
102 | { | ||
103 | u16 bMode, fracMode, aModeRefSel = 0; | ||
104 | u32 freq, ndiv, channelSel = 0, channelFrac = 0, reg32 = 0; | ||
105 | struct chan_centers centers; | ||
106 | u32 refDivA = 24; | ||
107 | |||
108 | ath9k_hw_get_channel_centers(ah, chan, ¢ers); | ||
109 | freq = centers.synth_center; | ||
110 | |||
111 | reg32 = REG_READ(ah, AR_PHY_SYNTH_CONTROL); | ||
112 | reg32 &= 0xc0000000; | ||
113 | |||
114 | if (freq < 4800) { | ||
115 | u32 txctl; | ||
116 | |||
117 | bMode = 1; | ||
118 | fracMode = 1; | ||
119 | aModeRefSel = 0; | ||
120 | channelSel = (freq * 0x10000) / 15; | ||
121 | |||
122 | txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL); | ||
123 | if (freq == 2484) { | ||
124 | |||
125 | REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, | ||
126 | txctl | AR_PHY_CCK_TX_CTRL_JAPAN); | ||
127 | } else { | ||
128 | REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, | ||
129 | txctl & ~AR_PHY_CCK_TX_CTRL_JAPAN); | ||
130 | } | ||
131 | } else { | ||
132 | bMode = 0; | ||
133 | fracMode = 0; | ||
134 | |||
135 | switch(ah->eep_ops->get_eeprom(ah, EEP_FRAC_N_5G)) { | ||
136 | case 0: | ||
137 | if ((freq % 20) == 0) { | ||
138 | aModeRefSel = 3; | ||
139 | } else if ((freq % 10) == 0) { | ||
140 | aModeRefSel = 2; | ||
141 | } | ||
142 | if (aModeRefSel) | ||
143 | break; | ||
144 | case 1: | ||
145 | default: | ||
146 | aModeRefSel = 0; | ||
147 | fracMode = 1; | ||
148 | refDivA = 1; | ||
149 | channelSel = (freq * 0x8000) / 15; | ||
150 | |||
151 | REG_RMW_FIELD(ah, AR_AN_SYNTH9, | ||
152 | AR_AN_SYNTH9_REFDIVA, refDivA); | ||
153 | |||
154 | } | ||
155 | |||
156 | if (!fracMode) { | ||
157 | ndiv = (freq * (refDivA >> aModeRefSel)) / 60; | ||
158 | channelSel = ndiv & 0x1ff; | ||
159 | channelFrac = (ndiv & 0xfffffe00) * 2; | ||
160 | channelSel = (channelSel << 17) | channelFrac; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | reg32 = reg32 | | ||
165 | (bMode << 29) | | ||
166 | (fracMode << 28) | (aModeRefSel << 26) | (channelSel); | ||
167 | |||
168 | REG_WRITE(ah, AR_PHY_SYNTH_CONTROL, reg32); | ||
169 | |||
170 | ah->curchan = chan; | ||
171 | ah->curchan_rad_index = -1; | ||
172 | |||
173 | return true; | ||
174 | } | ||
175 | |||
176 | static void | ||
177 | ath9k_phy_modify_rx_buffer(u32 *rfBuf, u32 reg32, | ||
178 | u32 numBits, u32 firstBit, | ||
179 | u32 column) | ||
180 | { | ||
181 | u32 tmp32, mask, arrayEntry, lastBit; | ||
182 | int32_t bitPosition, bitsLeft; | ||
183 | |||
184 | tmp32 = ath9k_hw_reverse_bits(reg32, numBits); | ||
185 | arrayEntry = (firstBit - 1) / 8; | ||
186 | bitPosition = (firstBit - 1) % 8; | ||
187 | bitsLeft = numBits; | ||
188 | while (bitsLeft > 0) { | ||
189 | lastBit = (bitPosition + bitsLeft > 8) ? | ||
190 | 8 : bitPosition + bitsLeft; | ||
191 | mask = (((1 << lastBit) - 1) ^ ((1 << bitPosition) - 1)) << | ||
192 | (column * 8); | ||
193 | rfBuf[arrayEntry] &= ~mask; | ||
194 | rfBuf[arrayEntry] |= ((tmp32 << bitPosition) << | ||
195 | (column * 8)) & mask; | ||
196 | bitsLeft -= 8 - bitPosition; | ||
197 | tmp32 = tmp32 >> (8 - bitPosition); | ||
198 | bitPosition = 0; | ||
199 | arrayEntry++; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | bool | ||
204 | ath9k_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan, | ||
205 | u16 modesIndex) | ||
206 | { | ||
207 | u32 eepMinorRev; | ||
208 | u32 ob5GHz = 0, db5GHz = 0; | ||
209 | u32 ob2GHz = 0, db2GHz = 0; | ||
210 | int regWrites = 0; | ||
211 | |||
212 | if (AR_SREV_9280_10_OR_LATER(ah)) | ||
213 | return true; | ||
214 | |||
215 | eepMinorRev = ah->eep_ops->get_eeprom(ah, EEP_MINOR_REV); | ||
216 | |||
217 | RF_BANK_SETUP(ah->analogBank0Data, &ah->iniBank0, 1); | ||
218 | |||
219 | RF_BANK_SETUP(ah->analogBank1Data, &ah->iniBank1, 1); | ||
220 | |||
221 | RF_BANK_SETUP(ah->analogBank2Data, &ah->iniBank2, 1); | ||
222 | |||
223 | RF_BANK_SETUP(ah->analogBank3Data, &ah->iniBank3, | ||
224 | modesIndex); | ||
225 | { | ||
226 | int i; | ||
227 | for (i = 0; i < ah->iniBank6TPC.ia_rows; i++) { | ||
228 | ah->analogBank6Data[i] = | ||
229 | INI_RA(&ah->iniBank6TPC, i, modesIndex); | ||
230 | } | ||
231 | } | ||
232 | |||
233 | if (eepMinorRev >= 2) { | ||
234 | if (IS_CHAN_2GHZ(chan)) { | ||
235 | ob2GHz = ah->eep_ops->get_eeprom(ah, EEP_OB_2); | ||
236 | db2GHz = ah->eep_ops->get_eeprom(ah, EEP_DB_2); | ||
237 | ath9k_phy_modify_rx_buffer(ah->analogBank6Data, | ||
238 | ob2GHz, 3, 197, 0); | ||
239 | ath9k_phy_modify_rx_buffer(ah->analogBank6Data, | ||
240 | db2GHz, 3, 194, 0); | ||
241 | } else { | ||
242 | ob5GHz = ah->eep_ops->get_eeprom(ah, EEP_OB_5); | ||
243 | db5GHz = ah->eep_ops->get_eeprom(ah, EEP_DB_5); | ||
244 | ath9k_phy_modify_rx_buffer(ah->analogBank6Data, | ||
245 | ob5GHz, 3, 203, 0); | ||
246 | ath9k_phy_modify_rx_buffer(ah->analogBank6Data, | ||
247 | db5GHz, 3, 200, 0); | ||
248 | } | ||
249 | } | ||
250 | |||
251 | RF_BANK_SETUP(ah->analogBank7Data, &ah->iniBank7, 1); | ||
252 | |||
253 | REG_WRITE_RF_ARRAY(&ah->iniBank0, ah->analogBank0Data, | ||
254 | regWrites); | ||
255 | REG_WRITE_RF_ARRAY(&ah->iniBank1, ah->analogBank1Data, | ||
256 | regWrites); | ||
257 | REG_WRITE_RF_ARRAY(&ah->iniBank2, ah->analogBank2Data, | ||
258 | regWrites); | ||
259 | REG_WRITE_RF_ARRAY(&ah->iniBank3, ah->analogBank3Data, | ||
260 | regWrites); | ||
261 | REG_WRITE_RF_ARRAY(&ah->iniBank6TPC, ah->analogBank6Data, | ||
262 | regWrites); | ||
263 | REG_WRITE_RF_ARRAY(&ah->iniBank7, ah->analogBank7Data, | ||
264 | regWrites); | ||
265 | |||
266 | return true; | ||
267 | } | ||
268 | |||
269 | void | ||
270 | ath9k_hw_rfdetach(struct ath_hw *ah) | ||
271 | { | ||
272 | if (ah->analogBank0Data != NULL) { | ||
273 | kfree(ah->analogBank0Data); | ||
274 | ah->analogBank0Data = NULL; | ||
275 | } | ||
276 | if (ah->analogBank1Data != NULL) { | ||
277 | kfree(ah->analogBank1Data); | ||
278 | ah->analogBank1Data = NULL; | ||
279 | } | ||
280 | if (ah->analogBank2Data != NULL) { | ||
281 | kfree(ah->analogBank2Data); | ||
282 | ah->analogBank2Data = NULL; | ||
283 | } | ||
284 | if (ah->analogBank3Data != NULL) { | ||
285 | kfree(ah->analogBank3Data); | ||
286 | ah->analogBank3Data = NULL; | ||
287 | } | ||
288 | if (ah->analogBank6Data != NULL) { | ||
289 | kfree(ah->analogBank6Data); | ||
290 | ah->analogBank6Data = NULL; | ||
291 | } | ||
292 | if (ah->analogBank6TPCData != NULL) { | ||
293 | kfree(ah->analogBank6TPCData); | ||
294 | ah->analogBank6TPCData = NULL; | ||
295 | } | ||
296 | if (ah->analogBank7Data != NULL) { | ||
297 | kfree(ah->analogBank7Data); | ||
298 | ah->analogBank7Data = NULL; | ||
299 | } | ||
300 | if (ah->addac5416_21 != NULL) { | ||
301 | kfree(ah->addac5416_21); | ||
302 | ah->addac5416_21 = NULL; | ||
303 | } | ||
304 | if (ah->bank6Temp != NULL) { | ||
305 | kfree(ah->bank6Temp); | ||
306 | ah->bank6Temp = NULL; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | bool ath9k_hw_init_rf(struct ath_hw *ah, int *status) | ||
311 | { | ||
312 | if (!AR_SREV_9280_10_OR_LATER(ah)) { | ||
313 | ah->analogBank0Data = | ||
314 | kzalloc((sizeof(u32) * | ||
315 | ah->iniBank0.ia_rows), GFP_KERNEL); | ||
316 | ah->analogBank1Data = | ||
317 | kzalloc((sizeof(u32) * | ||
318 | ah->iniBank1.ia_rows), GFP_KERNEL); | ||
319 | ah->analogBank2Data = | ||
320 | kzalloc((sizeof(u32) * | ||
321 | ah->iniBank2.ia_rows), GFP_KERNEL); | ||
322 | ah->analogBank3Data = | ||
323 | kzalloc((sizeof(u32) * | ||
324 | ah->iniBank3.ia_rows), GFP_KERNEL); | ||
325 | ah->analogBank6Data = | ||
326 | kzalloc((sizeof(u32) * | ||
327 | ah->iniBank6.ia_rows), GFP_KERNEL); | ||
328 | ah->analogBank6TPCData = | ||
329 | kzalloc((sizeof(u32) * | ||
330 | ah->iniBank6TPC.ia_rows), GFP_KERNEL); | ||
331 | ah->analogBank7Data = | ||
332 | kzalloc((sizeof(u32) * | ||
333 | ah->iniBank7.ia_rows), GFP_KERNEL); | ||
334 | |||
335 | if (ah->analogBank0Data == NULL | ||
336 | || ah->analogBank1Data == NULL | ||
337 | || ah->analogBank2Data == NULL | ||
338 | || ah->analogBank3Data == NULL | ||
339 | || ah->analogBank6Data == NULL | ||
340 | || ah->analogBank6TPCData == NULL | ||
341 | || ah->analogBank7Data == NULL) { | ||
342 | DPRINTF(ah->ah_sc, ATH_DBG_FATAL, | ||
343 | "Cannot allocate RF banks\n"); | ||
344 | *status = -ENOMEM; | ||
345 | return false; | ||
346 | } | ||
347 | |||
348 | ah->addac5416_21 = | ||
349 | kzalloc((sizeof(u32) * | ||
350 | ah->iniAddac.ia_rows * | ||
351 | ah->iniAddac.ia_columns), GFP_KERNEL); | ||
352 | if (ah->addac5416_21 == NULL) { | ||
353 | DPRINTF(ah->ah_sc, ATH_DBG_FATAL, | ||
354 | "Cannot allocate addac5416_21\n"); | ||
355 | *status = -ENOMEM; | ||
356 | return false; | ||
357 | } | ||
358 | |||
359 | ah->bank6Temp = | ||
360 | kzalloc((sizeof(u32) * | ||
361 | ah->iniBank6.ia_rows), GFP_KERNEL); | ||
362 | if (ah->bank6Temp == NULL) { | ||
363 | DPRINTF(ah->ah_sc, ATH_DBG_FATAL, | ||
364 | "Cannot allocate bank6Temp\n"); | ||
365 | *status = -ENOMEM; | ||
366 | return false; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | return true; | ||
371 | } | ||
372 | |||
373 | void | ||
374 | ath9k_hw_decrease_chain_power(struct ath_hw *ah, struct ath9k_channel *chan) | ||
375 | { | ||
376 | int i, regWrites = 0; | ||
377 | u32 bank6SelMask; | ||
378 | u32 *bank6Temp = ah->bank6Temp; | ||
379 | |||
380 | switch (ah->diversity_control) { | ||
381 | case ATH9K_ANT_FIXED_A: | ||
382 | bank6SelMask = | ||
383 | (ah-> | ||
384 | antenna_switch_swap & ANTSWAP_AB) ? REDUCE_CHAIN_0 : | ||
385 | REDUCE_CHAIN_1; | ||
386 | break; | ||
387 | case ATH9K_ANT_FIXED_B: | ||
388 | bank6SelMask = | ||
389 | (ah-> | ||
390 | antenna_switch_swap & ANTSWAP_AB) ? REDUCE_CHAIN_1 : | ||
391 | REDUCE_CHAIN_0; | ||
392 | break; | ||
393 | case ATH9K_ANT_VARIABLE: | ||
394 | return; | ||
395 | break; | ||
396 | default: | ||
397 | return; | ||
398 | break; | ||
399 | } | ||
400 | |||
401 | for (i = 0; i < ah->iniBank6.ia_rows; i++) | ||
402 | bank6Temp[i] = ah->analogBank6Data[i]; | ||
403 | |||
404 | REG_WRITE(ah, AR_PHY_BASE + 0xD8, bank6SelMask); | ||
405 | |||
406 | ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 189, 0); | ||
407 | ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 190, 0); | ||
408 | ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 191, 0); | ||
409 | ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 192, 0); | ||
410 | ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 193, 0); | ||
411 | ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 222, 0); | ||
412 | ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 245, 0); | ||
413 | ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 246, 0); | ||
414 | ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 247, 0); | ||
415 | |||
416 | REG_WRITE_RF_ARRAY(&ah->iniBank6, bank6Temp, regWrites); | ||
417 | |||
418 | REG_WRITE(ah, AR_PHY_BASE + 0xD8, 0x00000053); | ||
419 | #ifdef ALTER_SWITCH | ||
420 | REG_WRITE(ah, PHY_SWITCH_CHAIN_0, | ||
421 | (REG_READ(ah, PHY_SWITCH_CHAIN_0) & ~0x38) | ||
422 | | ((REG_READ(ah, PHY_SWITCH_CHAIN_0) >> 3) & 0x38)); | ||
423 | #endif | ||
424 | } | ||