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 | ||
394cf0a1 | 17 | #include "ath9k.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 | ||
b5aec950 | 86 | bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) |
f1dc5600 | 87 | { |
9dbeb91a GJ |
88 | struct ath_softc *sc = ah->ah_sc; |
89 | ||
90 | return sc->bus_ops->eeprom_read(ah, off, data); | |
f1dc5600 S |
91 | } |
92 | ||
b5aec950 S |
93 | void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, |
94 | u8 *pVpdList, u16 numIntercepts, | |
95 | u8 *pRetVpdList) | |
f74df6fb S |
96 | { |
97 | u16 i, k; | |
98 | u8 currPwr = pwrMin; | |
99 | u16 idxL = 0, idxR = 0; | |
100 | ||
101 | for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) { | |
102 | ath9k_hw_get_lower_upper_index(currPwr, pPwrList, | |
103 | numIntercepts, &(idxL), | |
104 | &(idxR)); | |
105 | if (idxR < 1) | |
106 | idxR = 1; | |
107 | if (idxL == numIntercepts - 1) | |
108 | idxL = (u16) (numIntercepts - 2); | |
109 | if (pPwrList[idxL] == pPwrList[idxR]) | |
110 | k = pVpdList[idxL]; | |
111 | else | |
112 | k = (u16)(((currPwr - pPwrList[idxL]) * pVpdList[idxR] + | |
113 | (pPwrList[idxR] - currPwr) * pVpdList[idxL]) / | |
114 | (pPwrList[idxR] - pPwrList[idxL])); | |
115 | pRetVpdList[i] = (u8) k; | |
116 | currPwr += 2; | |
117 | } | |
f74df6fb S |
118 | } |
119 | ||
b5aec950 S |
120 | void ath9k_hw_get_legacy_target_powers(struct ath_hw *ah, |
121 | struct ath9k_channel *chan, | |
122 | struct cal_target_power_leg *powInfo, | |
123 | u16 numChannels, | |
124 | struct cal_target_power_leg *pNewPower, | |
125 | u16 numRates, bool isExtTarget) | |
f74df6fb S |
126 | { |
127 | struct chan_centers centers; | |
128 | u16 clo, chi; | |
129 | int i; | |
130 | int matchIndex = -1, lowIndex = -1; | |
131 | u16 freq; | |
132 | ||
133 | ath9k_hw_get_channel_centers(ah, chan, ¢ers); | |
134 | freq = (isExtTarget) ? centers.ext_center : centers.ctl_center; | |
135 | ||
136 | if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel, | |
137 | IS_CHAN_2GHZ(chan))) { | |
138 | matchIndex = 0; | |
139 | } else { | |
140 | for (i = 0; (i < numChannels) && | |
141 | (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { | |
142 | if (freq == ath9k_hw_fbin2freq(powInfo[i].bChannel, | |
143 | IS_CHAN_2GHZ(chan))) { | |
144 | matchIndex = i; | |
145 | break; | |
146 | } else if ((freq < ath9k_hw_fbin2freq(powInfo[i].bChannel, | |
147 | IS_CHAN_2GHZ(chan))) && | |
148 | (freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel, | |
149 | IS_CHAN_2GHZ(chan)))) { | |
150 | lowIndex = i - 1; | |
151 | break; | |
152 | } | |
153 | } | |
154 | if ((matchIndex == -1) && (lowIndex == -1)) | |
155 | matchIndex = i - 1; | |
156 | } | |
157 | ||
158 | if (matchIndex != -1) { | |
159 | *pNewPower = powInfo[matchIndex]; | |
160 | } else { | |
161 | clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel, | |
162 | IS_CHAN_2GHZ(chan)); | |
163 | chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel, | |
164 | IS_CHAN_2GHZ(chan)); | |
165 | ||
166 | for (i = 0; i < numRates; i++) { | |
167 | pNewPower->tPow2x[i] = | |
168 | (u8)ath9k_hw_interpolate(freq, clo, chi, | |
169 | powInfo[lowIndex].tPow2x[i], | |
170 | powInfo[lowIndex + 1].tPow2x[i]); | |
171 | } | |
172 | } | |
173 | } | |
174 | ||
b5aec950 S |
175 | void ath9k_hw_get_target_powers(struct ath_hw *ah, |
176 | struct ath9k_channel *chan, | |
177 | struct cal_target_power_ht *powInfo, | |
178 | u16 numChannels, | |
179 | struct cal_target_power_ht *pNewPower, | |
180 | u16 numRates, bool isHt40Target) | |
f74df6fb S |
181 | { |
182 | struct chan_centers centers; | |
183 | u16 clo, chi; | |
184 | int i; | |
185 | int matchIndex = -1, lowIndex = -1; | |
186 | u16 freq; | |
187 | ||
188 | ath9k_hw_get_channel_centers(ah, chan, ¢ers); | |
189 | freq = isHt40Target ? centers.synth_center : centers.ctl_center; | |
190 | ||
191 | if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel, IS_CHAN_2GHZ(chan))) { | |
192 | matchIndex = 0; | |
193 | } else { | |
194 | for (i = 0; (i < numChannels) && | |
195 | (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { | |
196 | if (freq == ath9k_hw_fbin2freq(powInfo[i].bChannel, | |
197 | IS_CHAN_2GHZ(chan))) { | |
198 | matchIndex = i; | |
199 | break; | |
200 | } else | |
201 | if ((freq < ath9k_hw_fbin2freq(powInfo[i].bChannel, | |
202 | IS_CHAN_2GHZ(chan))) && | |
203 | (freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel, | |
204 | IS_CHAN_2GHZ(chan)))) { | |
205 | lowIndex = i - 1; | |
206 | break; | |
207 | } | |
208 | } | |
209 | if ((matchIndex == -1) && (lowIndex == -1)) | |
210 | matchIndex = i - 1; | |
211 | } | |
212 | ||
213 | if (matchIndex != -1) { | |
214 | *pNewPower = powInfo[matchIndex]; | |
215 | } else { | |
216 | clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel, | |
217 | IS_CHAN_2GHZ(chan)); | |
218 | chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel, | |
219 | IS_CHAN_2GHZ(chan)); | |
220 | ||
221 | for (i = 0; i < numRates; i++) { | |
222 | pNewPower->tPow2x[i] = (u8)ath9k_hw_interpolate(freq, | |
223 | clo, chi, | |
224 | powInfo[lowIndex].tPow2x[i], | |
225 | powInfo[lowIndex + 1].tPow2x[i]); | |
226 | } | |
227 | } | |
228 | } | |
229 | ||
b5aec950 S |
230 | u16 ath9k_hw_get_max_edge_power(u16 freq, struct cal_ctl_edges *pRdEdgesPower, |
231 | bool is2GHz, int num_band_edges) | |
f74df6fb S |
232 | { |
233 | u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER; | |
234 | int i; | |
235 | ||
236 | for (i = 0; (i < num_band_edges) && | |
237 | (pRdEdgesPower[i].bChannel != AR5416_BCHAN_UNUSED); i++) { | |
238 | if (freq == ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel, is2GHz)) { | |
239 | twiceMaxEdgePower = pRdEdgesPower[i].tPower; | |
240 | break; | |
241 | } else if ((i > 0) && | |
242 | (freq < ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel, | |
243 | is2GHz))) { | |
244 | if (ath9k_hw_fbin2freq(pRdEdgesPower[i - 1].bChannel, | |
245 | is2GHz) < freq && | |
246 | pRdEdgesPower[i - 1].flag) { | |
247 | twiceMaxEdgePower = | |
248 | pRdEdgesPower[i - 1].tPower; | |
249 | } | |
250 | break; | |
251 | } | |
252 | } | |
253 | ||
254 | return twiceMaxEdgePower; | |
255 | } | |
256 | ||
f637cfd6 | 257 | int ath9k_hw_eeprom_init(struct ath_hw *ah) |
f1dc5600 S |
258 | { |
259 | int status; | |
c16c9d06 | 260 | |
ac88b6ec VN |
261 | if (AR_SREV_9287(ah)) { |
262 | ah->eep_map = EEP_MAP_AR9287; | |
263 | ah->eep_ops = &eep_AR9287_ops; | |
d7e7d229 | 264 | } else if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) { |
2660b81a | 265 | ah->eep_map = EEP_MAP_4KBITS; |
f74df6fb S |
266 | ah->eep_ops = &eep_4k_ops; |
267 | } else { | |
2660b81a | 268 | ah->eep_map = EEP_MAP_DEFAULT; |
f74df6fb S |
269 | ah->eep_ops = &eep_def_ops; |
270 | } | |
e7594072 | 271 | |
f74df6fb | 272 | if (!ah->eep_ops->fill_eeprom(ah)) |
f1dc5600 S |
273 | return -EIO; |
274 | ||
f74df6fb | 275 | status = ah->eep_ops->check_eeprom(ah); |
f1dc5600 S |
276 | |
277 | return status; | |
278 | } |