diff options
author | Bob Copeland <me@bobcopeland.com> | 2009-10-14 14:16:30 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-10-27 16:48:18 -0400 |
commit | e5e2647fd6ceef2cdc479954b84517535eb7febd (patch) | |
tree | f0b22db14bae1881ea5cb4a9c6825192ac4a6afc /drivers/net/wireless/ath/ath5k/phy.c | |
parent | e307fcf0a10f9c0c21b3d8b2ff7862b29796cc7f (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.c | 185 |
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 | ||
1129 | static 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 | /** | 1135 | static 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. | 1143 | void 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 | 1152 | static 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 | |||
1159 | static 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 | */ |
1152 | int | 1193 | void ath5k_hw_update_noise_floor(struct ath5k_hw *ah) |
1153 | ath5k_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); |