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 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) 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 37 if (ah->config.analog_shiftreg) 38 udelay(100); 39 40 return; 41 } 42 43 int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight, 44 int16_t targetLeft, int16_t targetRight) 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 58 bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize, 59 u16 *indexL, u16 *indexR) 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 86 bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) 87 { 88 struct ath_softc *sc = ah->ah_sc; 89 90 return sc->bus_ops->eeprom_read(ah, off, data); 91 } 92 93 void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, 94 u8 *pVpdList, u16 numIntercepts, 95 u8 *pRetVpdList) 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 } 118 } 119 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) 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)) && i > 0 && 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 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) 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)) && i > 0 && 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 230 u16 ath9k_hw_get_max_edge_power(u16 freq, struct cal_ctl_edges *pRdEdgesPower, 231 bool is2GHz, int num_band_edges) 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 257 int ath9k_hw_eeprom_init(struct ath_hw *ah) 258 { 259 int status; 260 261 if (AR_SREV_9287(ah)) { 262 ah->eep_map = EEP_MAP_AR9287; 263 ah->eep_ops = &eep_AR9287_ops; 264 } else if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) { 265 ah->eep_map = EEP_MAP_4KBITS; 266 ah->eep_ops = &eep_4k_ops; 267 } else { 268 ah->eep_map = EEP_MAP_DEFAULT; 269 ah->eep_ops = &eep_def_ops; 270 } 271 272 if (!ah->eep_ops->fill_eeprom(ah)) 273 return -EIO; 274 275 status = ah->eep_ops->check_eeprom(ah); 276 277 return status; 278 } 279