Commit | Line | Data |
---|---|---|
f1dc5600 | 1 | /* |
cee075a2 | 2 | * Copyright (c) 2008-2009 Atheros Communications Inc. |
f1dc5600 S |
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 | ||
5bb12791 | 17 | #include "hw.h" |
f1dc5600 | 18 | |
b5aec950 S |
19 | static inline u16 ath9k_hw_fbin2freq(u8 fbin, bool is2GHz) |
20 | { | |
21 | if (fbin == AR5416_BCHAN_UNUSED) | |
22 | return fbin; | |
23 | ||
24 | return (u16) ((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin)); | |
25 | } | |
26 | ||
27 | void ath9k_hw_analog_shift_rmw(struct ath_hw *ah, u32 reg, u32 mask, | |
28 | u32 shift, u32 val) | |
f1dc5600 S |
29 | { |
30 | u32 regVal; | |
31 | ||
32 | regVal = REG_READ(ah, reg) & ~mask; | |
33 | regVal |= (val << shift) & mask; | |
34 | ||
35 | REG_WRITE(ah, reg, regVal); | |
36 | ||
2660b81a | 37 | if (ah->config.analog_shiftreg) |
f1dc5600 S |
38 | udelay(100); |
39 | ||
40 | return; | |
41 | } | |
42 | ||
b5aec950 S |
43 | int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight, |
44 | int16_t targetLeft, int16_t targetRight) | |
f1dc5600 S |
45 | { |
46 | int16_t rv; | |
47 | ||
48 | if (srcRight == srcLeft) { | |
49 | rv = targetLeft; | |
50 | } else { | |
51 | rv = (int16_t) (((target - srcLeft) * targetRight + | |
52 | (srcRight - target) * targetLeft) / | |
53 | (srcRight - srcLeft)); | |
54 | } | |
55 | return rv; | |
56 | } | |
57 | ||
b5aec950 S |
58 | bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize, |
59 | u16 *indexL, u16 *indexR) | |
f1dc5600 S |
60 | { |
61 | u16 i; | |
62 | ||
63 | if (target <= pList[0]) { | |
64 | *indexL = *indexR = 0; | |
65 | return true; | |
66 | } | |
67 | if (target >= pList[listSize - 1]) { | |
68 | *indexL = *indexR = (u16) (listSize - 1); | |
69 | return true; | |
70 | } | |
71 | ||
72 | for (i = 0; i < listSize - 1; i++) { | |
73 | if (pList[i] == target) { | |
74 | *indexL = *indexR = i; | |
75 | return true; | |
76 | } | |
77 | if (target < pList[i + 1]) { | |
78 | *indexL = i; | |
79 | *indexR = (u16) (i + 1); | |
80 | return false; | |
81 | } | |
82 | } | |
83 | return false; | |
84 | } | |
85 | ||
5bb12791 | 86 | bool ath9k_hw_nvram_read(struct ath_common *common, u32 off, u16 *data) |
f1dc5600 | 87 | { |
5bb12791 | 88 | return common->bus_ops->eeprom_read(common, off, data); |
f1dc5600 S |
89 | } |
90 | ||
b5aec950 S |
91 | void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, |
92 | u8 *pVpdList, u16 numIntercepts, | |
93 | u8 *pRetVpdList) | |
f74df6fb S |
94 | { |
95 | u16 i, k; | |
96 | u8 currPwr = pwrMin; | |
97 | u16 idxL = 0, idxR = 0; | |
98 | ||
99 | for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) { | |
100 | ath9k_hw_get_lower_upper_index(currPwr, pPwrList, | |
101 | numIntercepts, &(idxL), | |
102 | &(idxR)); | |
103 | if (idxR < 1) | |
104 | idxR = 1; | |
105 | if (idxL == numIntercepts - 1) | |
106 | idxL = (u16) (numIntercepts - 2); | |
107 | if (pPwrList[idxL] == pPwrList[idxR]) | |
108 | k = pVpdList[idxL]; | |
109 | else | |
110 | k = (u16)(((currPwr - pPwrList[idxL]) * pVpdList[idxR] + | |
111 | (pPwrList[idxR] - currPwr) * pVpdList[idxL]) / | |
112 | (pPwrList[idxR] - pPwrList[idxL])); | |
113 | pRetVpdList[i] = (u8) k; | |
114 | currPwr += 2; | |
115 | } | |
f74df6fb S |
116 | } |
117 | ||
b5aec950 S |
118 | void ath9k_hw_get_legacy_target_powers(struct ath_hw *ah, |
119 | struct ath9k_channel *chan, | |
120 | struct cal_target_power_leg *powInfo, | |
121 | u16 numChannels, | |
122 | struct cal_target_power_leg *pNewPower, | |
123 | u16 numRates, bool isExtTarget) | |
f74df6fb S |
124 | { |
125 | struct chan_centers centers; | |
126 | u16 clo, chi; | |
127 | int i; | |
128 | int matchIndex = -1, lowIndex = -1; | |
129 | u16 freq; | |
130 | ||
131 | ath9k_hw_get_channel_centers(ah, chan, ¢ers); | |
132 | freq = (isExtTarget) ? centers.ext_center : centers.ctl_center; | |
133 | ||
134 | if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel, | |
135 | IS_CHAN_2GHZ(chan))) { | |
136 | matchIndex = 0; | |
137 | } else { | |
138 | for (i = 0; (i < numChannels) && | |
139 | (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { | |
140 | if (freq == ath9k_hw_fbin2freq(powInfo[i].bChannel, | |
141 | IS_CHAN_2GHZ(chan))) { | |
142 | matchIndex = i; | |
143 | break; | |
73f57f83 RK |
144 | } else if (freq < ath9k_hw_fbin2freq(powInfo[i].bChannel, |
145 | IS_CHAN_2GHZ(chan)) && i > 0 && | |
146 | freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel, | |
147 | IS_CHAN_2GHZ(chan))) { | |
f74df6fb S |
148 | lowIndex = i - 1; |
149 | break; | |
150 | } | |
151 | } | |
152 | if ((matchIndex == -1) && (lowIndex == -1)) | |
153 | matchIndex = i - 1; | |
154 | } | |
155 | ||
156 | if (matchIndex != -1) { | |
157 | *pNewPower = powInfo[matchIndex]; | |
158 | } else { | |
159 | clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel, | |
160 | IS_CHAN_2GHZ(chan)); | |
161 | chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel, | |
162 | IS_CHAN_2GHZ(chan)); | |
163 | ||
164 | for (i = 0; i < numRates; i++) { | |
165 | pNewPower->tPow2x[i] = | |
166 | (u8)ath9k_hw_interpolate(freq, clo, chi, | |
167 | powInfo[lowIndex].tPow2x[i], | |
168 | powInfo[lowIndex + 1].tPow2x[i]); | |
169 | } | |
170 | } | |
171 | } | |
172 | ||
b5aec950 S |
173 | void ath9k_hw_get_target_powers(struct ath_hw *ah, |
174 | struct ath9k_channel *chan, | |
175 | struct cal_target_power_ht *powInfo, | |
176 | u16 numChannels, | |
177 | struct cal_target_power_ht *pNewPower, | |
178 | u16 numRates, bool isHt40Target) | |
f74df6fb S |
179 | { |
180 | struct chan_centers centers; | |
181 | u16 clo, chi; | |
182 | int i; | |
183 | int matchIndex = -1, lowIndex = -1; | |
184 | u16 freq; | |
185 | ||
186 | ath9k_hw_get_channel_centers(ah, chan, ¢ers); | |
187 | freq = isHt40Target ? centers.synth_center : centers.ctl_center; | |
188 | ||
189 | if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel, IS_CHAN_2GHZ(chan))) { | |
190 | matchIndex = 0; | |
191 | } else { | |
192 | for (i = 0; (i < numChannels) && | |
193 | (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { | |
194 | if (freq == ath9k_hw_fbin2freq(powInfo[i].bChannel, | |
195 | IS_CHAN_2GHZ(chan))) { | |
196 | matchIndex = i; | |
197 | break; | |
198 | } else | |
73f57f83 RK |
199 | if (freq < ath9k_hw_fbin2freq(powInfo[i].bChannel, |
200 | IS_CHAN_2GHZ(chan)) && i > 0 && | |
201 | freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel, | |
202 | IS_CHAN_2GHZ(chan))) { | |
f74df6fb S |
203 | lowIndex = i - 1; |
204 | break; | |
205 | } | |
206 | } | |
207 | if ((matchIndex == -1) && (lowIndex == -1)) | |
208 | matchIndex = i - 1; | |
209 | } | |
210 | ||
211 | if (matchIndex != -1) { | |
212 | *pNewPower = powInfo[matchIndex]; | |
213 | } else { | |
214 | clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel, | |
215 | IS_CHAN_2GHZ(chan)); | |
216 | chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel, | |
217 | IS_CHAN_2GHZ(chan)); | |
218 | ||
219 | for (i = 0; i < numRates; i++) { | |
220 | pNewPower->tPow2x[i] = (u8)ath9k_hw_interpolate(freq, | |
221 | clo, chi, | |
222 | powInfo[lowIndex].tPow2x[i], | |
223 | powInfo[lowIndex + 1].tPow2x[i]); | |
224 | } | |
225 | } | |
226 | } | |
227 | ||
b5aec950 S |
228 | u16 ath9k_hw_get_max_edge_power(u16 freq, struct cal_ctl_edges *pRdEdgesPower, |
229 | bool is2GHz, int num_band_edges) | |
f74df6fb S |
230 | { |
231 | u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER; | |
232 | int i; | |
233 | ||
234 | for (i = 0; (i < num_band_edges) && | |
235 | (pRdEdgesPower[i].bChannel != AR5416_BCHAN_UNUSED); i++) { | |
236 | if (freq == ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel, is2GHz)) { | |
237 | twiceMaxEdgePower = pRdEdgesPower[i].tPower; | |
238 | break; | |
239 | } else if ((i > 0) && | |
240 | (freq < ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel, | |
241 | is2GHz))) { | |
242 | if (ath9k_hw_fbin2freq(pRdEdgesPower[i - 1].bChannel, | |
243 | is2GHz) < freq && | |
244 | pRdEdgesPower[i - 1].flag) { | |
245 | twiceMaxEdgePower = | |
246 | pRdEdgesPower[i - 1].tPower; | |
247 | } | |
248 | break; | |
249 | } | |
250 | } | |
251 | ||
252 | return twiceMaxEdgePower; | |
253 | } | |
254 | ||
f637cfd6 | 255 | int ath9k_hw_eeprom_init(struct ath_hw *ah) |
f1dc5600 S |
256 | { |
257 | int status; | |
c16c9d06 | 258 | |
15c9ee7a SB |
259 | if (AR_SREV_9300_20_OR_LATER(ah)) |
260 | ah->eep_ops = &eep_ar9300_ops; | |
261 | else if (AR_SREV_9287(ah)) { | |
0b8f6f2b | 262 | ah->eep_ops = &eep_ar9287_ops; |
d7e7d229 | 263 | } else if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) { |
f74df6fb S |
264 | ah->eep_ops = &eep_4k_ops; |
265 | } else { | |
f74df6fb S |
266 | ah->eep_ops = &eep_def_ops; |
267 | } | |
e7594072 | 268 | |
f74df6fb | 269 | if (!ah->eep_ops->fill_eeprom(ah)) |
f1dc5600 S |
270 | return -EIO; |
271 | ||
f74df6fb | 272 | status = ah->eep_ops->check_eeprom(ah); |
f1dc5600 S |
273 | |
274 | return status; | |
275 | } |