diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
commit | ada47b5fe13d89735805b566185f4885f5a3f750 (patch) | |
tree | 644b88f8a71896307d71438e9b3af49126ffb22b /drivers/net/wireless/ath/ath5k/phy.c | |
parent | 43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff) | |
parent | 3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff) |
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'drivers/net/wireless/ath/ath5k/phy.c')
-rw-r--r-- | drivers/net/wireless/ath/ath5k/phy.c | 235 |
1 files changed, 153 insertions, 82 deletions
diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index 1a039f2bd732..68e2bccd90d3 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #define _ATH5K_PHY | 23 | #define _ATH5K_PHY |
24 | 24 | ||
25 | #include <linux/delay.h> | 25 | #include <linux/delay.h> |
26 | #include <linux/slab.h> | ||
26 | 27 | ||
27 | #include "ath5k.h" | 28 | #include "ath5k.h" |
28 | #include "reg.h" | 29 | #include "reg.h" |
@@ -117,7 +118,7 @@ static unsigned int ath5k_hw_rfb_op(struct ath5k_hw *ah, | |||
117 | 118 | ||
118 | /* | 119 | /* |
119 | * This code is used to optimize rf gain on different environments | 120 | * This code is used to optimize rf gain on different environments |
120 | * (temprature mostly) based on feedback from a power detector. | 121 | * (temperature mostly) based on feedback from a power detector. |
121 | * | 122 | * |
122 | * It's only used on RF5111 and RF5112, later RF chips seem to have | 123 | * It's only used on RF5111 and RF5112, later RF chips seem to have |
123 | * auto adjustment on hw -notice they have a much smaller BANK 7 and | 124 | * auto adjustment on hw -notice they have a much smaller BANK 7 and |
@@ -1124,77 +1125,148 @@ ath5k_hw_calibration_poll(struct ath5k_hw *ah) | |||
1124 | ah->ah_swi_mask = AR5K_SWI_FULL_CALIBRATION; | 1125 | ah->ah_swi_mask = AR5K_SWI_FULL_CALIBRATION; |
1125 | AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); | 1126 | AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); |
1126 | } | 1127 | } |
1128 | } | ||
1127 | 1129 | ||
1130 | static int sign_extend(int val, const int nbits) | ||
1131 | { | ||
1132 | int order = BIT(nbits-1); | ||
1133 | return (val ^ order) - order; | ||
1128 | } | 1134 | } |
1129 | 1135 | ||
1130 | /** | 1136 | static s32 ath5k_hw_read_measured_noise_floor(struct ath5k_hw *ah) |
1131 | * ath5k_hw_noise_floor_calibration - perform PHY noise floor calibration | 1137 | { |
1132 | * | 1138 | s32 val; |
1133 | * @ah: struct ath5k_hw pointer we are operating on | 1139 | |
1134 | * @freq: the channel frequency, just used for error logging | 1140 | val = ath5k_hw_reg_read(ah, AR5K_PHY_NF); |
1135 | * | 1141 | return sign_extend(AR5K_REG_MS(val, AR5K_PHY_NF_MINCCA_PWR), 9); |
1136 | * This function performs a noise floor calibration of the PHY and waits for | 1142 | } |
1137 | * it to complete. Then the noise floor value is compared to some maximum | 1143 | |
1138 | * noise floor we consider valid. | 1144 | void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah) |
1139 | * | 1145 | { |
1140 | * Note that this is different from what the madwifi HAL does: it reads the | 1146 | int i; |
1141 | * noise floor and afterwards initiates the calibration. Since the noise floor | 1147 | |
1142 | * calibration can take some time to finish, depending on the current channel | 1148 | ah->ah_nfcal_hist.index = 0; |
1143 | * use, that avoids the occasional timeout warnings we are seeing now. | 1149 | for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++) |
1144 | * | 1150 | ah->ah_nfcal_hist.nfval[i] = AR5K_TUNE_CCA_MAX_GOOD_VALUE; |
1145 | * See the following link for an Atheros patent on noise floor calibration: | 1151 | } |
1146 | * http://patft.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&Sect2=HITOFF&d=PALL \ | 1152 | |
1147 | * &p=1&u=%2Fnetahtml%2FPTO%2Fsrchnum.htm&r=1&f=G&l=50&s1=7245893.PN.&OS=PN/7 | 1153 | static void ath5k_hw_update_nfcal_hist(struct ath5k_hw *ah, s16 noise_floor) |
1154 | { | ||
1155 | struct ath5k_nfcal_hist *hist = &ah->ah_nfcal_hist; | ||
1156 | hist->index = (hist->index + 1) & (ATH5K_NF_CAL_HIST_MAX-1); | ||
1157 | hist->nfval[hist->index] = noise_floor; | ||
1158 | } | ||
1159 | |||
1160 | static s16 ath5k_hw_get_median_noise_floor(struct ath5k_hw *ah) | ||
1161 | { | ||
1162 | s16 sort[ATH5K_NF_CAL_HIST_MAX]; | ||
1163 | s16 tmp; | ||
1164 | int i, j; | ||
1165 | |||
1166 | memcpy(sort, ah->ah_nfcal_hist.nfval, sizeof(sort)); | ||
1167 | for (i = 0; i < ATH5K_NF_CAL_HIST_MAX - 1; i++) { | ||
1168 | for (j = 1; j < ATH5K_NF_CAL_HIST_MAX - i; j++) { | ||
1169 | if (sort[j] > sort[j-1]) { | ||
1170 | tmp = sort[j]; | ||
1171 | sort[j] = sort[j-1]; | ||
1172 | sort[j-1] = tmp; | ||
1173 | } | ||
1174 | } | ||
1175 | } | ||
1176 | for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++) { | ||
1177 | ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, | ||
1178 | "cal %d:%d\n", i, sort[i]); | ||
1179 | } | ||
1180 | return sort[(ATH5K_NF_CAL_HIST_MAX-1) / 2]; | ||
1181 | } | ||
1182 | |||
1183 | /* | ||
1184 | * When we tell the hardware to perform a noise floor calibration | ||
1185 | * by setting the AR5K_PHY_AGCCTL_NF bit, it will periodically | ||
1186 | * sample-and-hold the minimum noise level seen at the antennas. | ||
1187 | * This value is then stored in a ring buffer of recently measured | ||
1188 | * noise floor values so we have a moving window of the last few | ||
1189 | * samples. | ||
1148 | * | 1190 | * |
1149 | * XXX: Since during noise floor calibration antennas are detached according to | 1191 | * The median of the values in the history is then loaded into the |
1150 | * the patent, we should stop tx queues here. | 1192 | * hardware for its own use for RSSI and CCA measurements. |
1151 | */ | 1193 | */ |
1152 | int | 1194 | void ath5k_hw_update_noise_floor(struct ath5k_hw *ah) |
1153 | ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq) | ||
1154 | { | 1195 | { |
1155 | int ret; | 1196 | struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; |
1156 | unsigned int i; | 1197 | u32 val; |
1157 | s32 noise_floor; | 1198 | s16 nf, threshold; |
1199 | u8 ee_mode; | ||
1158 | 1200 | ||
1159 | /* | 1201 | /* keep last value if calibration hasn't completed */ |
1160 | * Enable noise floor calibration | 1202 | if (ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL) & AR5K_PHY_AGCCTL_NF) { |
1161 | */ | 1203 | ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, |
1162 | AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, | 1204 | "NF did not complete in calibration window\n"); |
1163 | AR5K_PHY_AGCCTL_NF); | ||
1164 | 1205 | ||
1165 | ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, | 1206 | return; |
1166 | AR5K_PHY_AGCCTL_NF, 0, false); | ||
1167 | if (ret) { | ||
1168 | ATH5K_ERR(ah->ah_sc, | ||
1169 | "noise floor calibration timeout (%uMHz)\n", freq); | ||
1170 | return -EAGAIN; | ||
1171 | } | 1207 | } |
1172 | 1208 | ||
1173 | /* Wait until the noise floor is calibrated and read the value */ | 1209 | switch (ah->ah_current_channel->hw_value & CHANNEL_MODES) { |
1174 | for (i = 20; i > 0; i--) { | 1210 | case CHANNEL_A: |
1175 | mdelay(1); | 1211 | case CHANNEL_T: |
1176 | noise_floor = ath5k_hw_reg_read(ah, AR5K_PHY_NF); | 1212 | case CHANNEL_XR: |
1177 | noise_floor = AR5K_PHY_NF_RVAL(noise_floor); | 1213 | ee_mode = AR5K_EEPROM_MODE_11A; |
1178 | if (noise_floor & AR5K_PHY_NF_ACTIVE) { | 1214 | break; |
1179 | noise_floor = AR5K_PHY_NF_AVAL(noise_floor); | 1215 | case CHANNEL_G: |
1180 | 1216 | case CHANNEL_TG: | |
1181 | if (noise_floor <= AR5K_TUNE_NOISE_FLOOR) | 1217 | ee_mode = AR5K_EEPROM_MODE_11G; |
1182 | break; | 1218 | break; |
1183 | } | 1219 | default: |
1220 | case CHANNEL_B: | ||
1221 | ee_mode = AR5K_EEPROM_MODE_11B; | ||
1222 | break; | ||
1184 | } | 1223 | } |
1185 | 1224 | ||
1186 | ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, | ||
1187 | "noise floor %d\n", noise_floor); | ||
1188 | 1225 | ||
1189 | if (noise_floor > AR5K_TUNE_NOISE_FLOOR) { | 1226 | /* completed NF calibration, test threshold */ |
1190 | ATH5K_ERR(ah->ah_sc, | 1227 | nf = ath5k_hw_read_measured_noise_floor(ah); |
1191 | "noise floor calibration failed (%uMHz)\n", freq); | 1228 | threshold = ee->ee_noise_floor_thr[ee_mode]; |
1192 | return -EAGAIN; | 1229 | |
1230 | if (nf > threshold) { | ||
1231 | ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, | ||
1232 | "noise floor failure detected; " | ||
1233 | "read %d, threshold %d\n", | ||
1234 | nf, threshold); | ||
1235 | |||
1236 | nf = AR5K_TUNE_CCA_MAX_GOOD_VALUE; | ||
1193 | } | 1237 | } |
1194 | 1238 | ||
1195 | ah->ah_noise_floor = noise_floor; | 1239 | ath5k_hw_update_nfcal_hist(ah, nf); |
1240 | nf = ath5k_hw_get_median_noise_floor(ah); | ||
1196 | 1241 | ||
1197 | return 0; | 1242 | /* load noise floor (in .5 dBm) so the hardware will use it */ |
1243 | val = ath5k_hw_reg_read(ah, AR5K_PHY_NF) & ~AR5K_PHY_NF_M; | ||
1244 | val |= (nf * 2) & AR5K_PHY_NF_M; | ||
1245 | ath5k_hw_reg_write(ah, val, AR5K_PHY_NF); | ||
1246 | |||
1247 | AR5K_REG_MASKED_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF, | ||
1248 | ~(AR5K_PHY_AGCCTL_NF_EN | AR5K_PHY_AGCCTL_NF_NOUPDATE)); | ||
1249 | |||
1250 | ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF, | ||
1251 | 0, false); | ||
1252 | |||
1253 | /* | ||
1254 | * Load a high max CCA Power value (-50 dBm in .5 dBm units) | ||
1255 | * so that we're not capped by the median we just loaded. | ||
1256 | * This will be used as the initial value for the next noise | ||
1257 | * floor calibration. | ||
1258 | */ | ||
1259 | val = (val & ~AR5K_PHY_NF_M) | ((-50 * 2) & AR5K_PHY_NF_M); | ||
1260 | ath5k_hw_reg_write(ah, val, AR5K_PHY_NF); | ||
1261 | AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, | ||
1262 | AR5K_PHY_AGCCTL_NF_EN | | ||
1263 | AR5K_PHY_AGCCTL_NF_NOUPDATE | | ||
1264 | AR5K_PHY_AGCCTL_NF); | ||
1265 | |||
1266 | ah->ah_noise_floor = nf; | ||
1267 | |||
1268 | ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, | ||
1269 | "noise floor calibrated: %d\n", nf); | ||
1198 | } | 1270 | } |
1199 | 1271 | ||
1200 | /* | 1272 | /* |
@@ -1287,7 +1359,7 @@ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, | |||
1287 | return ret; | 1359 | return ret; |
1288 | } | 1360 | } |
1289 | 1361 | ||
1290 | ath5k_hw_noise_floor_calibration(ah, channel->center_freq); | 1362 | ath5k_hw_update_noise_floor(ah); |
1291 | 1363 | ||
1292 | /* | 1364 | /* |
1293 | * Re-enable RX/TX and beacons | 1365 | * Re-enable RX/TX and beacons |
@@ -1315,38 +1387,39 @@ static int ath5k_hw_rf511x_calibrate(struct ath5k_hw *ah, | |||
1315 | goto done; | 1387 | goto done; |
1316 | 1388 | ||
1317 | /* Calibration has finished, get the results and re-run */ | 1389 | /* Calibration has finished, get the results and re-run */ |
1390 | |||
1391 | /* work around empty results which can apparently happen on 5212 */ | ||
1318 | for (i = 0; i <= 10; i++) { | 1392 | for (i = 0; i <= 10; i++) { |
1319 | iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR); | 1393 | iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR); |
1320 | i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I); | 1394 | i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I); |
1321 | q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q); | 1395 | q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q); |
1396 | ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, | ||
1397 | "iq_corr:%x i_pwr:%x q_pwr:%x", iq_corr, i_pwr, q_pwr); | ||
1398 | if (i_pwr && q_pwr) | ||
1399 | break; | ||
1322 | } | 1400 | } |
1323 | 1401 | ||
1324 | i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7; | 1402 | i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7; |
1325 | q_coffd = q_pwr >> 7; | 1403 | q_coffd = q_pwr >> 7; |
1326 | 1404 | ||
1327 | /* No correction */ | 1405 | /* protect against divide by 0 and loss of sign bits */ |
1328 | if (i_coffd == 0 || q_coffd == 0) | 1406 | if (i_coffd == 0 || q_coffd < 2) |
1329 | goto done; | 1407 | goto done; |
1330 | 1408 | ||
1331 | i_coff = ((-iq_corr) / i_coffd) & 0x3f; | 1409 | i_coff = (-iq_corr) / i_coffd; |
1410 | i_coff = clamp(i_coff, -32, 31); /* signed 6 bit */ | ||
1332 | 1411 | ||
1333 | /* Boundary check */ | 1412 | q_coff = (i_pwr / q_coffd) - 128; |
1334 | if (i_coff > 31) | 1413 | q_coff = clamp(q_coff, -16, 15); /* signed 5 bit */ |
1335 | i_coff = 31; | ||
1336 | if (i_coff < -32) | ||
1337 | i_coff = -32; | ||
1338 | 1414 | ||
1339 | q_coff = (((s32)i_pwr / q_coffd) - 128) & 0x1f; | 1415 | ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, |
1340 | 1416 | "new I:%d Q:%d (i_coffd:%x q_coffd:%x)", | |
1341 | /* Boundary check */ | 1417 | i_coff, q_coff, i_coffd, q_coffd); |
1342 | if (q_coff > 15) | ||
1343 | q_coff = 15; | ||
1344 | if (q_coff < -16) | ||
1345 | q_coff = -16; | ||
1346 | 1418 | ||
1347 | /* Commit new I/Q value */ | 1419 | /* Commit new I/Q values (set enable bit last to match HAL sources) */ |
1348 | AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE | | 1420 | AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_I_COFF, i_coff); |
1349 | ((u32)q_coff) | ((u32)i_coff << AR5K_PHY_IQ_CORR_Q_I_COFF_S)); | 1421 | AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_Q_COFF, q_coff); |
1422 | AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE); | ||
1350 | 1423 | ||
1351 | /* Re-enable calibration -if we don't we'll commit | 1424 | /* Re-enable calibration -if we don't we'll commit |
1352 | * the same values again and again */ | 1425 | * the same values again and again */ |
@@ -1360,7 +1433,7 @@ done: | |||
1360 | * since noise floor calibration interrupts rx path while I/Q | 1433 | * since noise floor calibration interrupts rx path while I/Q |
1361 | * calibration doesn't. We don't need to run noise floor calibration | 1434 | * calibration doesn't. We don't need to run noise floor calibration |
1362 | * as often as I/Q calibration.*/ | 1435 | * as often as I/Q calibration.*/ |
1363 | ath5k_hw_noise_floor_calibration(ah, channel->center_freq); | 1436 | ath5k_hw_update_noise_floor(ah); |
1364 | 1437 | ||
1365 | /* Initiate a gain_F calibration */ | 1438 | /* Initiate a gain_F calibration */ |
1366 | ath5k_hw_request_rfgain_probe(ah); | 1439 | ath5k_hw_request_rfgain_probe(ah); |
@@ -1802,7 +1875,7 @@ ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode) | |||
1802 | break; | 1875 | break; |
1803 | case AR5K_ANTMODE_FIXED_A: | 1876 | case AR5K_ANTMODE_FIXED_A: |
1804 | def_ant = 1; | 1877 | def_ant = 1; |
1805 | tx_ant = 0; | 1878 | tx_ant = 1; |
1806 | use_def_for_tx = true; | 1879 | use_def_for_tx = true; |
1807 | update_def_on_tx = false; | 1880 | update_def_on_tx = false; |
1808 | use_def_for_rts = true; | 1881 | use_def_for_rts = true; |
@@ -1811,7 +1884,7 @@ ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode) | |||
1811 | break; | 1884 | break; |
1812 | case AR5K_ANTMODE_FIXED_B: | 1885 | case AR5K_ANTMODE_FIXED_B: |
1813 | def_ant = 2; | 1886 | def_ant = 2; |
1814 | tx_ant = 0; | 1887 | tx_ant = 2; |
1815 | use_def_for_tx = true; | 1888 | use_def_for_tx = true; |
1816 | update_def_on_tx = false; | 1889 | update_def_on_tx = false; |
1817 | use_def_for_rts = true; | 1890 | use_def_for_rts = true; |
@@ -2675,7 +2748,7 @@ ath5k_setup_channel_powertable(struct ath5k_hw *ah, | |||
2675 | /* Fill curves in reverse order | 2748 | /* Fill curves in reverse order |
2676 | * from lower power (max gain) | 2749 | * from lower power (max gain) |
2677 | * to higher power. Use curve -> idx | 2750 | * to higher power. Use curve -> idx |
2678 | * backmaping we did on eeprom init */ | 2751 | * backmapping we did on eeprom init */ |
2679 | u8 idx = pdg_curve_to_idx[pdg]; | 2752 | u8 idx = pdg_curve_to_idx[pdg]; |
2680 | 2753 | ||
2681 | /* Grab the needed curves by index */ | 2754 | /* Grab the needed curves by index */ |
@@ -2777,7 +2850,7 @@ ath5k_setup_channel_powertable(struct ath5k_hw *ah, | |||
2777 | /* Now we have a set of curves for this | 2850 | /* Now we have a set of curves for this |
2778 | * channel on tmpL (x range is table_max - table_min | 2851 | * channel on tmpL (x range is table_max - table_min |
2779 | * and y values are tmpL[pdg][]) sorted in the same | 2852 | * and y values are tmpL[pdg][]) sorted in the same |
2780 | * order as EEPROM (because we've used the backmaping). | 2853 | * order as EEPROM (because we've used the backmapping). |
2781 | * So for RF5112 it's from higher power to lower power | 2854 | * So for RF5112 it's from higher power to lower power |
2782 | * and for RF2413 it's from lower power to higher power. | 2855 | * and for RF2413 it's from lower power to higher power. |
2783 | * For RF5111 we only have one curve. */ | 2856 | * For RF5111 we only have one curve. */ |
@@ -2954,8 +3027,6 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, | |||
2954 | ATH5K_ERR(ah->ah_sc, "invalid tx power: %u\n", txpower); | 3027 | ATH5K_ERR(ah->ah_sc, "invalid tx power: %u\n", txpower); |
2955 | return -EINVAL; | 3028 | return -EINVAL; |
2956 | } | 3029 | } |
2957 | if (txpower == 0) | ||
2958 | txpower = AR5K_TUNE_DEFAULT_TXPOWER; | ||
2959 | 3030 | ||
2960 | /* Reset TX power values */ | 3031 | /* Reset TX power values */ |
2961 | memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower)); | 3032 | memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower)); |