1 | /* |
2 | * Copyright (c) 2008-2011 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 "hw.h" |
18 | #include <linux/ath9k_platform.h> |
19 | |
20 | void ath9k_hw_analog_shift_regwrite(struct ath_hw *ah, u32 reg, u32 val) |
21 | { |
22 | REG_WRITE(ah, reg, val); |
23 | |
24 | if (ah->config.analog_shiftreg) |
25 | udelay(100); |
26 | } |
27 | |
28 | void ath9k_hw_analog_shift_rmw(struct ath_hw *ah, u32 reg, u32 mask, |
29 | u32 shift, u32 val) |
30 | { |
31 | REG_RMW(ah, reg, ((val << shift) & mask), mask); |
32 | |
33 | if (ah->config.analog_shiftreg) |
34 | udelay(100); |
35 | } |
36 | |
37 | int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight, |
38 | int16_t targetLeft, int16_t targetRight) |
39 | { |
40 | int16_t rv; |
41 | |
42 | if (srcRight == srcLeft) { |
43 | rv = targetLeft; |
44 | } else { |
45 | rv = (int16_t) (((target - srcLeft) * targetRight + |
46 | (srcRight - target) * targetLeft) / |
47 | (srcRight - srcLeft)); |
48 | } |
49 | return rv; |
50 | } |
51 | |
52 | bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize, |
53 | u16 *indexL, u16 *indexR) |
54 | { |
55 | u16 i; |
56 | |
57 | if (target <= pList[0]) { |
58 | *indexL = *indexR = 0; |
59 | return true; |
60 | } |
61 | if (target >= pList[listSize - 1]) { |
62 | *indexL = *indexR = (u16) (listSize - 1); |
63 | return true; |
64 | } |
65 | |
66 | for (i = 0; i < listSize - 1; i++) { |
67 | if (pList[i] == target) { |
68 | *indexL = *indexR = i; |
69 | return true; |
70 | } |
71 | if (target < pList[i + 1]) { |
72 | *indexL = i; |
73 | *indexR = (u16) (i + 1); |
74 | return false; |
75 | } |
76 | } |
77 | return false; |
78 | } |
79 | |
80 | void ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data, |
81 | int eep_start_loc, int size) |
82 | { |
83 | int i = 0, j, addr; |
84 | u32 addrdata[8]; |
85 | u32 data[8]; |
86 | |
87 | for (addr = 0; addr < size; addr++) { |
88 | addrdata[i] = AR5416_EEPROM_OFFSET + |
89 | ((addr + eep_start_loc) << AR5416_EEPROM_S); |
90 | i++; |
91 | if (i == 8) { |
92 | REG_READ_MULTI(ah, addrdata, data, i); |
93 | |
94 | for (j = 0; j < i; j++) { |
95 | *eep_data = data[j]; |
96 | eep_data++; |
97 | } |
98 | i = 0; |
99 | } |
100 | } |
101 | |
102 | if (i != 0) { |
103 | REG_READ_MULTI(ah, addrdata, data, i); |
104 | |
105 | for (j = 0; j < i; j++) { |
106 | *eep_data = data[j]; |
107 | eep_data++; |
108 | } |
109 | } |
110 | } |
111 | |
112 | static bool ath9k_hw_nvram_read_array(u16 *blob, size_t blob_size, |
113 | off_t offset, u16 *data) |
114 | { |
115 | if (offset >= blob_size) |
116 | return false; |
117 | |
118 | *data = blob[offset]; |
119 | return true; |
120 | } |
121 | |
122 | static bool ath9k_hw_nvram_read_pdata(struct ath9k_platform_data *pdata, |
123 | off_t offset, u16 *data) |
124 | { |
125 | return ath9k_hw_nvram_read_array(blob: pdata->eeprom_data, |
126 | ARRAY_SIZE(pdata->eeprom_data), |
127 | offset, data); |
128 | } |
129 | |
130 | static bool ath9k_hw_nvram_read_firmware(const struct firmware *eeprom_blob, |
131 | off_t offset, u16 *data) |
132 | { |
133 | return ath9k_hw_nvram_read_array(blob: (u16 *) eeprom_blob->data, |
134 | blob_size: eeprom_blob->size / sizeof(u16), |
135 | offset, data); |
136 | } |
137 | |
138 | static bool ath9k_hw_nvram_read_nvmem(struct ath_hw *ah, off_t offset, |
139 | u16 *data) |
140 | { |
141 | return ath9k_hw_nvram_read_array(blob: ah->nvmem_blob, |
142 | blob_size: ah->nvmem_blob_len / sizeof(u16), |
143 | offset, data); |
144 | } |
145 | |
146 | bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) |
147 | { |
148 | struct ath_common *common = ath9k_hw_common(ah); |
149 | struct ath9k_platform_data *pdata = ah->dev->platform_data; |
150 | bool ret; |
151 | |
152 | if (ah->nvmem_blob) |
153 | ret = ath9k_hw_nvram_read_nvmem(ah, offset: off, data); |
154 | else if (ah->eeprom_blob) |
155 | ret = ath9k_hw_nvram_read_firmware(eeprom_blob: ah->eeprom_blob, offset: off, data); |
156 | else if (pdata && !pdata->use_eeprom) |
157 | ret = ath9k_hw_nvram_read_pdata(pdata, offset: off, data); |
158 | else |
159 | ret = common->bus_ops->eeprom_read(common, off, data); |
160 | |
161 | if (!ret) |
162 | ath_dbg(common, EEPROM, |
163 | "unable to read eeprom region at offset %u\n" , off); |
164 | |
165 | return ret; |
166 | } |
167 | |
168 | int ath9k_hw_nvram_swap_data(struct ath_hw *ah, bool *swap_needed, int size) |
169 | { |
170 | u16 magic; |
171 | u16 *eepdata; |
172 | int i; |
173 | bool needs_byteswap = false; |
174 | struct ath_common *common = ath9k_hw_common(ah); |
175 | |
176 | if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, data: &magic)) { |
177 | ath_err(common, "Reading Magic # failed\n" ); |
178 | return -EIO; |
179 | } |
180 | |
181 | if (swab16(magic) == AR5416_EEPROM_MAGIC) { |
182 | needs_byteswap = true; |
183 | ath_dbg(common, EEPROM, |
184 | "EEPROM needs byte-swapping to correct endianness.\n" ); |
185 | } else if (magic != AR5416_EEPROM_MAGIC) { |
186 | if (ath9k_hw_use_flash(ah)) { |
187 | ath_dbg(common, EEPROM, |
188 | "Ignoring invalid EEPROM magic (0x%04x).\n" , |
189 | magic); |
190 | } else { |
191 | ath_err(common, |
192 | "Invalid EEPROM magic (0x%04x).\n" , magic); |
193 | return -EINVAL; |
194 | } |
195 | } |
196 | |
197 | if (needs_byteswap) { |
198 | if (ah->ah_flags & AH_NO_EEP_SWAP) { |
199 | ath_info(common, |
200 | "Ignoring endianness difference in EEPROM magic bytes.\n" ); |
201 | } else { |
202 | eepdata = (u16 *)(&ah->eeprom); |
203 | |
204 | for (i = 0; i < size; i++) |
205 | eepdata[i] = swab16(eepdata[i]); |
206 | } |
207 | } |
208 | |
209 | if (ah->eep_ops->get_eepmisc(ah) & AR5416_EEPMISC_BIG_ENDIAN) { |
210 | *swap_needed = true; |
211 | ath_dbg(common, EEPROM, |
212 | "Big Endian EEPROM detected according to EEPMISC register.\n" ); |
213 | } else { |
214 | *swap_needed = false; |
215 | } |
216 | |
217 | return 0; |
218 | } |
219 | |
220 | bool ath9k_hw_nvram_validate_checksum(struct ath_hw *ah, int size) |
221 | { |
222 | u32 i, sum = 0; |
223 | u16 *eepdata = (u16 *)(&ah->eeprom); |
224 | struct ath_common *common = ath9k_hw_common(ah); |
225 | |
226 | for (i = 0; i < size; i++) |
227 | sum ^= eepdata[i]; |
228 | |
229 | if (sum != 0xffff) { |
230 | ath_err(common, "Bad EEPROM checksum 0x%x\n" , sum); |
231 | return false; |
232 | } |
233 | |
234 | return true; |
235 | } |
236 | |
237 | bool ath9k_hw_nvram_check_version(struct ath_hw *ah, int version, int minrev) |
238 | { |
239 | struct ath_common *common = ath9k_hw_common(ah); |
240 | |
241 | if (ah->eep_ops->get_eeprom_ver(ah) != version || |
242 | ah->eep_ops->get_eeprom_rev(ah) < minrev) { |
243 | ath_err(common, "Bad EEPROM VER 0x%04x or REV 0x%04x\n" , |
244 | ah->eep_ops->get_eeprom_ver(ah), |
245 | ah->eep_ops->get_eeprom_rev(ah)); |
246 | return false; |
247 | } |
248 | |
249 | return true; |
250 | } |
251 | |
252 | void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, |
253 | u8 *pVpdList, u16 numIntercepts, |
254 | u8 *pRetVpdList) |
255 | { |
256 | u16 i, k; |
257 | u8 currPwr = pwrMin; |
258 | u16 idxL = 0, idxR = 0; |
259 | |
260 | for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) { |
261 | ath9k_hw_get_lower_upper_index(target: currPwr, pList: pPwrList, |
262 | listSize: numIntercepts, indexL: &(idxL), |
263 | indexR: &(idxR)); |
264 | if (idxR < 1) |
265 | idxR = 1; |
266 | if (idxL == numIntercepts - 1) |
267 | idxL = (u16) (numIntercepts - 2); |
268 | if (pPwrList[idxL] == pPwrList[idxR]) |
269 | k = pVpdList[idxL]; |
270 | else |
271 | k = (u16)(((currPwr - pPwrList[idxL]) * pVpdList[idxR] + |
272 | (pPwrList[idxR] - currPwr) * pVpdList[idxL]) / |
273 | (pPwrList[idxR] - pPwrList[idxL])); |
274 | pRetVpdList[i] = (u8) k; |
275 | currPwr += 2; |
276 | } |
277 | } |
278 | |
279 | void ath9k_hw_get_legacy_target_powers(struct ath_hw *ah, |
280 | struct ath9k_channel *chan, |
281 | struct cal_target_power_leg *powInfo, |
282 | u16 numChannels, |
283 | struct cal_target_power_leg *pNewPower, |
284 | u16 numRates, bool isExtTarget) |
285 | { |
286 | struct chan_centers centers; |
287 | u16 clo, chi; |
288 | int i; |
289 | int matchIndex = -1, lowIndex = -1; |
290 | u16 freq; |
291 | |
292 | ath9k_hw_get_channel_centers(ah, chan, centers: ¢ers); |
293 | freq = (isExtTarget) ? centers.ext_center : centers.ctl_center; |
294 | |
295 | if (freq <= ath9k_hw_fbin2freq(fbin: powInfo[0].bChannel, |
296 | IS_CHAN_2GHZ(chan))) { |
297 | matchIndex = 0; |
298 | } else { |
299 | for (i = 0; (i < numChannels) && |
300 | (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { |
301 | if (freq == ath9k_hw_fbin2freq(fbin: powInfo[i].bChannel, |
302 | IS_CHAN_2GHZ(chan))) { |
303 | matchIndex = i; |
304 | break; |
305 | } else if (freq < ath9k_hw_fbin2freq(fbin: powInfo[i].bChannel, |
306 | IS_CHAN_2GHZ(chan)) && i > 0 && |
307 | freq > ath9k_hw_fbin2freq(fbin: powInfo[i - 1].bChannel, |
308 | IS_CHAN_2GHZ(chan))) { |
309 | lowIndex = i - 1; |
310 | break; |
311 | } |
312 | } |
313 | if ((matchIndex == -1) && (lowIndex == -1)) |
314 | matchIndex = i - 1; |
315 | } |
316 | |
317 | if (matchIndex != -1) { |
318 | *pNewPower = powInfo[matchIndex]; |
319 | } else { |
320 | clo = ath9k_hw_fbin2freq(fbin: powInfo[lowIndex].bChannel, |
321 | IS_CHAN_2GHZ(chan)); |
322 | chi = ath9k_hw_fbin2freq(fbin: powInfo[lowIndex + 1].bChannel, |
323 | IS_CHAN_2GHZ(chan)); |
324 | |
325 | for (i = 0; i < numRates; i++) { |
326 | pNewPower->tPow2x[i] = |
327 | (u8)ath9k_hw_interpolate(target: freq, srcLeft: clo, srcRight: chi, |
328 | targetLeft: powInfo[lowIndex].tPow2x[i], |
329 | targetRight: powInfo[lowIndex + 1].tPow2x[i]); |
330 | } |
331 | } |
332 | } |
333 | |
334 | void ath9k_hw_get_target_powers(struct ath_hw *ah, |
335 | struct ath9k_channel *chan, |
336 | struct cal_target_power_ht *powInfo, |
337 | u16 numChannels, |
338 | struct cal_target_power_ht *pNewPower, |
339 | u16 numRates, bool isHt40Target) |
340 | { |
341 | struct chan_centers centers; |
342 | u16 clo, chi; |
343 | int i; |
344 | int matchIndex = -1, lowIndex = -1; |
345 | u16 freq; |
346 | |
347 | ath9k_hw_get_channel_centers(ah, chan, centers: ¢ers); |
348 | freq = isHt40Target ? centers.synth_center : centers.ctl_center; |
349 | |
350 | if (freq <= ath9k_hw_fbin2freq(fbin: powInfo[0].bChannel, IS_CHAN_2GHZ(chan))) { |
351 | matchIndex = 0; |
352 | } else { |
353 | for (i = 0; (i < numChannels) && |
354 | (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { |
355 | if (freq == ath9k_hw_fbin2freq(fbin: powInfo[i].bChannel, |
356 | IS_CHAN_2GHZ(chan))) { |
357 | matchIndex = i; |
358 | break; |
359 | } else |
360 | if (freq < ath9k_hw_fbin2freq(fbin: powInfo[i].bChannel, |
361 | IS_CHAN_2GHZ(chan)) && i > 0 && |
362 | freq > ath9k_hw_fbin2freq(fbin: powInfo[i - 1].bChannel, |
363 | IS_CHAN_2GHZ(chan))) { |
364 | lowIndex = i - 1; |
365 | break; |
366 | } |
367 | } |
368 | if ((matchIndex == -1) && (lowIndex == -1)) |
369 | matchIndex = i - 1; |
370 | } |
371 | |
372 | if (matchIndex != -1) { |
373 | *pNewPower = powInfo[matchIndex]; |
374 | } else { |
375 | clo = ath9k_hw_fbin2freq(fbin: powInfo[lowIndex].bChannel, |
376 | IS_CHAN_2GHZ(chan)); |
377 | chi = ath9k_hw_fbin2freq(fbin: powInfo[lowIndex + 1].bChannel, |
378 | IS_CHAN_2GHZ(chan)); |
379 | |
380 | for (i = 0; i < numRates; i++) { |
381 | pNewPower->tPow2x[i] = (u8)ath9k_hw_interpolate(target: freq, |
382 | srcLeft: clo, srcRight: chi, |
383 | targetLeft: powInfo[lowIndex].tPow2x[i], |
384 | targetRight: powInfo[lowIndex + 1].tPow2x[i]); |
385 | } |
386 | } |
387 | } |
388 | |
389 | u16 ath9k_hw_get_max_edge_power(u16 freq, struct cal_ctl_edges *pRdEdgesPower, |
390 | bool is2GHz, int num_band_edges) |
391 | { |
392 | u16 twiceMaxEdgePower = MAX_RATE_POWER; |
393 | int i; |
394 | |
395 | for (i = 0; (i < num_band_edges) && |
396 | (pRdEdgesPower[i].bChannel != AR5416_BCHAN_UNUSED); i++) { |
397 | if (freq == ath9k_hw_fbin2freq(fbin: pRdEdgesPower[i].bChannel, is2GHz)) { |
398 | twiceMaxEdgePower = CTL_EDGE_TPOWER(pRdEdgesPower[i].ctl); |
399 | break; |
400 | } else if ((i > 0) && |
401 | (freq < ath9k_hw_fbin2freq(fbin: pRdEdgesPower[i].bChannel, |
402 | is2GHz))) { |
403 | if (ath9k_hw_fbin2freq(fbin: pRdEdgesPower[i - 1].bChannel, |
404 | is2GHz) < freq && |
405 | CTL_EDGE_FLAGS(pRdEdgesPower[i - 1].ctl)) { |
406 | twiceMaxEdgePower = |
407 | CTL_EDGE_TPOWER(pRdEdgesPower[i - 1].ctl); |
408 | } |
409 | break; |
410 | } |
411 | } |
412 | |
413 | return twiceMaxEdgePower; |
414 | } |
415 | |
416 | u16 ath9k_hw_get_scaled_power(struct ath_hw *ah, u16 power_limit, |
417 | u8 antenna_reduction) |
418 | { |
419 | u16 reduction = antenna_reduction; |
420 | |
421 | /* |
422 | * Reduce scaled Power by number of chains active |
423 | * to get the per chain tx power level. |
424 | */ |
425 | switch (ar5416_get_ntxchains(ah->txchainmask)) { |
426 | case 1: |
427 | break; |
428 | case 2: |
429 | reduction += POWER_CORRECTION_FOR_TWO_CHAIN; |
430 | break; |
431 | case 3: |
432 | reduction += POWER_CORRECTION_FOR_THREE_CHAIN; |
433 | break; |
434 | } |
435 | |
436 | if (power_limit > reduction) |
437 | power_limit -= reduction; |
438 | else |
439 | power_limit = 0; |
440 | |
441 | return min_t(u16, power_limit, MAX_RATE_POWER); |
442 | } |
443 | |
444 | void ath9k_hw_update_regulatory_maxpower(struct ath_hw *ah) |
445 | { |
446 | struct ath_common *common = ath9k_hw_common(ah); |
447 | struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); |
448 | |
449 | switch (ar5416_get_ntxchains(ah->txchainmask)) { |
450 | case 1: |
451 | break; |
452 | case 2: |
453 | regulatory->max_power_level += POWER_CORRECTION_FOR_TWO_CHAIN; |
454 | break; |
455 | case 3: |
456 | regulatory->max_power_level += POWER_CORRECTION_FOR_THREE_CHAIN; |
457 | break; |
458 | default: |
459 | ath_dbg(common, EEPROM, "Invalid chainmask configuration\n" ); |
460 | break; |
461 | } |
462 | } |
463 | |
464 | void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, |
465 | struct ath9k_channel *chan, |
466 | void *pRawDataSet, |
467 | u8 *bChans, u16 availPiers, |
468 | u16 tPdGainOverlap, |
469 | u16 *pPdGainBoundaries, u8 *pPDADCValues, |
470 | u16 numXpdGains) |
471 | { |
472 | int i, j, k; |
473 | int16_t ss; |
474 | u16 idxL = 0, idxR = 0, numPiers; |
475 | static u8 vpdTableL[AR5416_NUM_PD_GAINS] |
476 | [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; |
477 | static u8 vpdTableR[AR5416_NUM_PD_GAINS] |
478 | [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; |
479 | static u8 vpdTableI[AR5416_NUM_PD_GAINS] |
480 | [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; |
481 | |
482 | u8 *pVpdL, *pVpdR, *pPwrL, *pPwrR; |
483 | u8 minPwrT4[AR5416_NUM_PD_GAINS]; |
484 | u8 maxPwrT4[AR5416_NUM_PD_GAINS]; |
485 | int16_t vpdStep; |
486 | int16_t tmpVal; |
487 | u16 sizeCurrVpdTable, maxIndex, tgtIndex; |
488 | bool match; |
489 | int16_t minDelta = 0; |
490 | struct chan_centers centers; |
491 | int pdgain_boundary_default; |
492 | struct cal_data_per_freq *data_def = pRawDataSet; |
493 | struct cal_data_per_freq_4k *data_4k = pRawDataSet; |
494 | struct cal_data_per_freq_ar9287 *data_9287 = pRawDataSet; |
495 | bool eeprom_4k = AR_SREV_9285(ah) || AR_SREV_9271(ah); |
496 | int intercepts; |
497 | |
498 | if (AR_SREV_9287(ah)) |
499 | intercepts = AR9287_PD_GAIN_ICEPTS; |
500 | else |
501 | intercepts = AR5416_PD_GAIN_ICEPTS; |
502 | |
503 | memset(&minPwrT4, 0, AR5416_NUM_PD_GAINS); |
504 | ath9k_hw_get_channel_centers(ah, chan, centers: ¢ers); |
505 | |
506 | for (numPiers = 0; numPiers < availPiers; numPiers++) { |
507 | if (bChans[numPiers] == AR5416_BCHAN_UNUSED) |
508 | break; |
509 | } |
510 | |
511 | match = ath9k_hw_get_lower_upper_index(target: (u8)FREQ2FBIN(centers.synth_center, |
512 | IS_CHAN_2GHZ(chan)), |
513 | pList: bChans, listSize: numPiers, indexL: &idxL, indexR: &idxR); |
514 | |
515 | if (match) { |
516 | if (AR_SREV_9287(ah)) { |
517 | for (i = 0; i < numXpdGains; i++) { |
518 | minPwrT4[i] = data_9287[idxL].pwrPdg[i][0]; |
519 | maxPwrT4[i] = data_9287[idxL].pwrPdg[i][intercepts - 1]; |
520 | ath9k_hw_fill_vpd_table(pwrMin: minPwrT4[i], pwrMax: maxPwrT4[i], |
521 | pPwrList: data_9287[idxL].pwrPdg[i], |
522 | pVpdList: data_9287[idxL].vpdPdg[i], |
523 | numIntercepts: intercepts, |
524 | pRetVpdList: vpdTableI[i]); |
525 | } |
526 | } else if (eeprom_4k) { |
527 | for (i = 0; i < numXpdGains; i++) { |
528 | minPwrT4[i] = data_4k[idxL].pwrPdg[i][0]; |
529 | maxPwrT4[i] = data_4k[idxL].pwrPdg[i][intercepts - 1]; |
530 | ath9k_hw_fill_vpd_table(pwrMin: minPwrT4[i], pwrMax: maxPwrT4[i], |
531 | pPwrList: data_4k[idxL].pwrPdg[i], |
532 | pVpdList: data_4k[idxL].vpdPdg[i], |
533 | numIntercepts: intercepts, |
534 | pRetVpdList: vpdTableI[i]); |
535 | } |
536 | } else { |
537 | for (i = 0; i < numXpdGains; i++) { |
538 | minPwrT4[i] = data_def[idxL].pwrPdg[i][0]; |
539 | maxPwrT4[i] = data_def[idxL].pwrPdg[i][intercepts - 1]; |
540 | ath9k_hw_fill_vpd_table(pwrMin: minPwrT4[i], pwrMax: maxPwrT4[i], |
541 | pPwrList: data_def[idxL].pwrPdg[i], |
542 | pVpdList: data_def[idxL].vpdPdg[i], |
543 | numIntercepts: intercepts, |
544 | pRetVpdList: vpdTableI[i]); |
545 | } |
546 | } |
547 | } else { |
548 | for (i = 0; i < numXpdGains; i++) { |
549 | if (AR_SREV_9287(ah)) { |
550 | pVpdL = data_9287[idxL].vpdPdg[i]; |
551 | pPwrL = data_9287[idxL].pwrPdg[i]; |
552 | pVpdR = data_9287[idxR].vpdPdg[i]; |
553 | pPwrR = data_9287[idxR].pwrPdg[i]; |
554 | } else if (eeprom_4k) { |
555 | pVpdL = data_4k[idxL].vpdPdg[i]; |
556 | pPwrL = data_4k[idxL].pwrPdg[i]; |
557 | pVpdR = data_4k[idxR].vpdPdg[i]; |
558 | pPwrR = data_4k[idxR].pwrPdg[i]; |
559 | } else { |
560 | pVpdL = data_def[idxL].vpdPdg[i]; |
561 | pPwrL = data_def[idxL].pwrPdg[i]; |
562 | pVpdR = data_def[idxR].vpdPdg[i]; |
563 | pPwrR = data_def[idxR].pwrPdg[i]; |
564 | } |
565 | |
566 | minPwrT4[i] = max(pPwrL[0], pPwrR[0]); |
567 | |
568 | maxPwrT4[i] = |
569 | min(pPwrL[intercepts - 1], |
570 | pPwrR[intercepts - 1]); |
571 | |
572 | |
573 | ath9k_hw_fill_vpd_table(pwrMin: minPwrT4[i], pwrMax: maxPwrT4[i], |
574 | pPwrList: pPwrL, pVpdList: pVpdL, |
575 | numIntercepts: intercepts, |
576 | pRetVpdList: vpdTableL[i]); |
577 | ath9k_hw_fill_vpd_table(pwrMin: minPwrT4[i], pwrMax: maxPwrT4[i], |
578 | pPwrList: pPwrR, pVpdList: pVpdR, |
579 | numIntercepts: intercepts, |
580 | pRetVpdList: vpdTableR[i]); |
581 | |
582 | for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) { |
583 | vpdTableI[i][j] = |
584 | (u8)(ath9k_hw_interpolate(target: (u16) |
585 | FREQ2FBIN(centers. |
586 | synth_center, |
587 | IS_CHAN_2GHZ |
588 | (chan)), |
589 | srcLeft: bChans[idxL], srcRight: bChans[idxR], |
590 | targetLeft: vpdTableL[i][j], targetRight: vpdTableR[i][j])); |
591 | } |
592 | } |
593 | } |
594 | |
595 | k = 0; |
596 | |
597 | for (i = 0; i < numXpdGains; i++) { |
598 | if (i == (numXpdGains - 1)) |
599 | pPdGainBoundaries[i] = |
600 | (u16)(maxPwrT4[i] / 2); |
601 | else |
602 | pPdGainBoundaries[i] = |
603 | (u16)((maxPwrT4[i] + minPwrT4[i + 1]) / 4); |
604 | |
605 | pPdGainBoundaries[i] = |
606 | min((u16)MAX_RATE_POWER, pPdGainBoundaries[i]); |
607 | |
608 | minDelta = 0; |
609 | |
610 | if (i == 0) { |
611 | if (AR_SREV_9280_20_OR_LATER(ah)) |
612 | ss = (int16_t)(0 - (minPwrT4[i] / 2)); |
613 | else |
614 | ss = 0; |
615 | } else { |
616 | ss = (int16_t)((pPdGainBoundaries[i - 1] - |
617 | (minPwrT4[i] / 2)) - |
618 | tPdGainOverlap + 1 + minDelta); |
619 | } |
620 | vpdStep = (int16_t)(vpdTableI[i][1] - vpdTableI[i][0]); |
621 | vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); |
622 | |
623 | while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { |
624 | tmpVal = (int16_t)(vpdTableI[i][0] + ss * vpdStep); |
625 | pPDADCValues[k++] = (u8)((tmpVal < 0) ? 0 : tmpVal); |
626 | ss++; |
627 | } |
628 | |
629 | sizeCurrVpdTable = (u8) ((maxPwrT4[i] - minPwrT4[i]) / 2 + 1); |
630 | tgtIndex = (u8)(pPdGainBoundaries[i] + tPdGainOverlap - |
631 | (minPwrT4[i] / 2)); |
632 | maxIndex = (tgtIndex < sizeCurrVpdTable) ? |
633 | tgtIndex : sizeCurrVpdTable; |
634 | |
635 | while ((ss < maxIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { |
636 | pPDADCValues[k++] = vpdTableI[i][ss++]; |
637 | } |
638 | |
639 | vpdStep = (int16_t)(vpdTableI[i][sizeCurrVpdTable - 1] - |
640 | vpdTableI[i][sizeCurrVpdTable - 2]); |
641 | vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); |
642 | |
643 | if (tgtIndex >= maxIndex) { |
644 | while ((ss <= tgtIndex) && |
645 | (k < (AR5416_NUM_PDADC_VALUES - 1))) { |
646 | tmpVal = (int16_t)((vpdTableI[i][sizeCurrVpdTable - 1] + |
647 | (ss - maxIndex + 1) * vpdStep)); |
648 | pPDADCValues[k++] = (u8)((tmpVal > 255) ? |
649 | 255 : tmpVal); |
650 | ss++; |
651 | } |
652 | } |
653 | } |
654 | |
655 | if (eeprom_4k) |
656 | pdgain_boundary_default = 58; |
657 | else |
658 | pdgain_boundary_default = pPdGainBoundaries[i - 1]; |
659 | |
660 | while (i < AR5416_PD_GAINS_IN_MASK) { |
661 | pPdGainBoundaries[i] = pdgain_boundary_default; |
662 | i++; |
663 | } |
664 | |
665 | while (k < AR5416_NUM_PDADC_VALUES) { |
666 | pPDADCValues[k] = pPDADCValues[k - 1]; |
667 | k++; |
668 | } |
669 | } |
670 | |
671 | int ath9k_hw_eeprom_init(struct ath_hw *ah) |
672 | { |
673 | if (AR_SREV_9300_20_OR_LATER(ah)) |
674 | ah->eep_ops = &eep_ar9300_ops; |
675 | else if (AR_SREV_9287(ah)) { |
676 | ah->eep_ops = &eep_ar9287_ops; |
677 | } else if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) { |
678 | ah->eep_ops = &eep_4k_ops; |
679 | } else { |
680 | ah->eep_ops = &eep_def_ops; |
681 | } |
682 | |
683 | if (!ah->eep_ops->fill_eeprom(ah)) |
684 | return -EIO; |
685 | |
686 | return ah->eep_ops->check_eeprom(ah); |
687 | } |
688 | |