aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath5k/phy.c
diff options
context:
space:
mode:
authorBob Copeland <me@bobcopeland.com>2009-10-14 14:16:30 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-10-27 16:48:18 -0400
commite5e2647fd6ceef2cdc479954b84517535eb7febd (patch)
treef0b22db14bae1881ea5cb4a9c6825192ac4a6afc /drivers/net/wireless/ath/ath5k/phy.c
parente307fcf0a10f9c0c21b3d8b2ff7862b29796cc7f (diff)
ath5k: use noise calibration from madwifi hal
This updates ath5k to calibrate the noise floor similar to the way it is done in the madwifi hal and ath9k. Of note: - we start NF measurement at the same time as AGC calibration, but do not actually read the value until the periodic (long) calibration - we keep a history of the last few values read and write the median back to the hardware for CCA - we do not complain if NF calibration isn't complete, instead we keep the last read value. Signed-off-by: Bob Copeland <me@bobcopeland.com> Acked-by: Nick Kossifidis <mickflemm@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath/ath5k/phy.c')
-rw-r--r--drivers/net/wireless/ath/ath5k/phy.c185
1 files changed, 128 insertions, 57 deletions
diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c
index 1a039f2bd732..895990751d36 100644
--- a/drivers/net/wireless/ath/ath5k/phy.c
+++ b/drivers/net/wireless/ath/ath5k/phy.c
@@ -1124,77 +1124,148 @@ ath5k_hw_calibration_poll(struct ath5k_hw *ah)
1124 ah->ah_swi_mask = AR5K_SWI_FULL_CALIBRATION; 1124 ah->ah_swi_mask = AR5K_SWI_FULL_CALIBRATION;
1125 AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); 1125 AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI);
1126 } 1126 }
1127}
1127 1128
1129static int sign_extend(int val, const int nbits)
1130{
1131 int order = BIT(nbits-1);
1132 return (val ^ order) - order;
1128} 1133}
1129 1134
1130/** 1135static s32 ath5k_hw_read_measured_noise_floor(struct ath5k_hw *ah)
1131 * ath5k_hw_noise_floor_calibration - perform PHY noise floor calibration 1136{
1132 * 1137 s32 val;
1133 * @ah: struct ath5k_hw pointer we are operating on 1138
1134 * @freq: the channel frequency, just used for error logging 1139 val = ath5k_hw_reg_read(ah, AR5K_PHY_NF);
1135 * 1140 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 1141}
1137 * it to complete. Then the noise floor value is compared to some maximum 1142
1138 * noise floor we consider valid. 1143void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah)
1139 * 1144{
1140 * Note that this is different from what the madwifi HAL does: it reads the 1145 int i;
1141 * noise floor and afterwards initiates the calibration. Since the noise floor 1146
1142 * calibration can take some time to finish, depending on the current channel 1147 ah->ah_nfcal_hist.index = 0;
1143 * use, that avoids the occasional timeout warnings we are seeing now. 1148 for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++)
1144 * 1149 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: 1150}
1146 * http://patft.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&Sect2=HITOFF&d=PALL \ 1151
1147 * &p=1&u=%2Fnetahtml%2FPTO%2Fsrchnum.htm&r=1&f=G&l=50&s1=7245893.PN.&OS=PN/7 1152static void ath5k_hw_update_nfcal_hist(struct ath5k_hw *ah, s16 noise_floor)
1153{
1154 struct ath5k_nfcal_hist *hist = &ah->ah_nfcal_hist;
1155 hist->index = (hist->index + 1) & (ATH5K_NF_CAL_HIST_MAX-1);
1156 hist->nfval[hist->index] = noise_floor;
1157}
1158
1159static s16 ath5k_hw_get_median_noise_floor(struct ath5k_hw *ah)
1160{
1161 s16 sort[ATH5K_NF_CAL_HIST_MAX];
1162 s16 tmp;
1163 int i, j;
1164
1165 memcpy(sort, ah->ah_nfcal_hist.nfval, sizeof(sort));
1166 for (i = 0; i < ATH5K_NF_CAL_HIST_MAX - 1; i++) {
1167 for (j = 1; j < ATH5K_NF_CAL_HIST_MAX - i; j++) {
1168 if (sort[j] > sort[j-1]) {
1169 tmp = sort[j];
1170 sort[j] = sort[j-1];
1171 sort[j-1] = tmp;
1172 }
1173 }
1174 }
1175 for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++) {
1176 ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
1177 "cal %d:%d\n", i, sort[i]);
1178 }
1179 return sort[(ATH5K_NF_CAL_HIST_MAX-1) / 2];
1180}
1181
1182/*
1183 * When we tell the hardware to perform a noise floor calibration
1184 * by setting the AR5K_PHY_AGCCTL_NF bit, it will periodically
1185 * sample-and-hold the minimum noise level seen at the antennas.
1186 * This value is then stored in a ring buffer of recently measured
1187 * noise floor values so we have a moving window of the last few
1188 * samples.
1148 * 1189 *
1149 * XXX: Since during noise floor calibration antennas are detached according to 1190 * The median of the values in the history is then loaded into the
1150 * the patent, we should stop tx queues here. 1191 * hardware for its own use for RSSI and CCA measurements.
1151 */ 1192 */
1152int 1193void ath5k_hw_update_noise_floor(struct ath5k_hw *ah)
1153ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq)
1154{ 1194{
1155 int ret; 1195 struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
1156 unsigned int i; 1196 u32 val;
1157 s32 noise_floor; 1197 s16 nf, threshold;
1198 u8 ee_mode;
1158 1199
1159 /* 1200 /* keep last value if calibration hasn't completed */
1160 * Enable noise floor calibration 1201 if (ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL) & AR5K_PHY_AGCCTL_NF) {
1161 */ 1202 ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
1162 AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, 1203 "NF did not complete in calibration window\n");
1163 AR5K_PHY_AGCCTL_NF);
1164 1204
1165 ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, 1205 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 } 1206 }
1172 1207
1173 /* Wait until the noise floor is calibrated and read the value */ 1208 switch (ah->ah_current_channel->hw_value & CHANNEL_MODES) {
1174 for (i = 20; i > 0; i--) { 1209 case CHANNEL_A:
1175 mdelay(1); 1210 case CHANNEL_T:
1176 noise_floor = ath5k_hw_reg_read(ah, AR5K_PHY_NF); 1211 case CHANNEL_XR:
1177 noise_floor = AR5K_PHY_NF_RVAL(noise_floor); 1212 ee_mode = AR5K_EEPROM_MODE_11A;
1178 if (noise_floor & AR5K_PHY_NF_ACTIVE) { 1213 break;
1179 noise_floor = AR5K_PHY_NF_AVAL(noise_floor); 1214 case CHANNEL_G:
1180 1215 case CHANNEL_TG:
1181 if (noise_floor <= AR5K_TUNE_NOISE_FLOOR) 1216 ee_mode = AR5K_EEPROM_MODE_11G;
1182 break; 1217 break;
1183 } 1218 default:
1219 case CHANNEL_B:
1220 ee_mode = AR5K_EEPROM_MODE_11B;
1221 break;
1184 } 1222 }
1185 1223
1186 ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
1187 "noise floor %d\n", noise_floor);
1188 1224
1189 if (noise_floor > AR5K_TUNE_NOISE_FLOOR) { 1225 /* completed NF calibration, test threshold */
1190 ATH5K_ERR(ah->ah_sc, 1226 nf = ath5k_hw_read_measured_noise_floor(ah);
1191 "noise floor calibration failed (%uMHz)\n", freq); 1227 threshold = ee->ee_noise_floor_thr[ee_mode];
1192 return -EAGAIN; 1228
1229 if (nf > threshold) {
1230 ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
1231 "noise floor failure detected; "
1232 "read %d, threshold %d\n",
1233 nf, threshold);
1234
1235 nf = AR5K_TUNE_CCA_MAX_GOOD_VALUE;
1193 } 1236 }
1194 1237
1195 ah->ah_noise_floor = noise_floor; 1238 ath5k_hw_update_nfcal_hist(ah, nf);
1239 nf = ath5k_hw_get_median_noise_floor(ah);
1196 1240
1197 return 0; 1241 /* load noise floor (in .5 dBm) so the hardware will use it */
1242 val = ath5k_hw_reg_read(ah, AR5K_PHY_NF) & ~AR5K_PHY_NF_M;
1243 val |= (nf * 2) & AR5K_PHY_NF_M;
1244 ath5k_hw_reg_write(ah, val, AR5K_PHY_NF);
1245
1246 AR5K_REG_MASKED_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF,
1247 ~(AR5K_PHY_AGCCTL_NF_EN | AR5K_PHY_AGCCTL_NF_NOUPDATE));
1248
1249 ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF,
1250 0, false);
1251
1252 /*
1253 * Load a high max CCA Power value (-50 dBm in .5 dBm units)
1254 * so that we're not capped by the median we just loaded.
1255 * This will be used as the initial value for the next noise
1256 * floor calibration.
1257 */
1258 val = (val & ~AR5K_PHY_NF_M) | ((-50 * 2) & AR5K_PHY_NF_M);
1259 ath5k_hw_reg_write(ah, val, AR5K_PHY_NF);
1260 AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
1261 AR5K_PHY_AGCCTL_NF_EN |
1262 AR5K_PHY_AGCCTL_NF_NOUPDATE |
1263 AR5K_PHY_AGCCTL_NF);
1264
1265 ah->ah_noise_floor = nf;
1266
1267 ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
1268 "noise floor calibrated: %d\n", nf);
1198} 1269}
1199 1270
1200/* 1271/*
@@ -1287,7 +1358,7 @@ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah,
1287 return ret; 1358 return ret;
1288 } 1359 }
1289 1360
1290 ath5k_hw_noise_floor_calibration(ah, channel->center_freq); 1361 ath5k_hw_update_noise_floor(ah);
1291 1362
1292 /* 1363 /*
1293 * Re-enable RX/TX and beacons 1364 * Re-enable RX/TX and beacons
@@ -1360,7 +1431,7 @@ done:
1360 * since noise floor calibration interrupts rx path while I/Q 1431 * since noise floor calibration interrupts rx path while I/Q
1361 * calibration doesn't. We don't need to run noise floor calibration 1432 * calibration doesn't. We don't need to run noise floor calibration
1362 * as often as I/Q calibration.*/ 1433 * as often as I/Q calibration.*/
1363 ath5k_hw_noise_floor_calibration(ah, channel->center_freq); 1434 ath5k_hw_update_noise_floor(ah);
1364 1435
1365 /* Initiate a gain_F calibration */ 1436 /* Initiate a gain_F calibration */
1366 ath5k_hw_request_rfgain_probe(ah); 1437 ath5k_hw_request_rfgain_probe(ah);