diff options
author | Nick Kossifidis <mick@madwifi.org> | 2008-07-19 23:41:26 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-08-01 15:31:32 -0400 |
commit | df75dcddf99647d68f3b6b874effe5365c5024d9 (patch) | |
tree | a1624d7e79ea0bb19ca4906507e3b10cef4d07c5 /drivers/net/wireless/ath5k | |
parent | e2a0ccebc4ffabc1c7234cfd324299b5a936e0f2 (diff) |
ath5k: Reorder calibration calls during reset and update hw_set_power
* Update ath5k_hw_reset and add some more documentation about PHY calibration
* Fix ath5k_hw_set_power to use AR5K_SLEEP_CTL_SLE_ALLOW for Network sleep
* Preserve sleep duration field while setting AR5K_SLEEP_CTL
and reduce delays & checks for register's status (got this from
decompiling & dumps, it works for me but it needs testing)
Changes-licensed-under: ISC
Signed-off-by: Nick Kossifidis <mickflemm@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath5k')
-rw-r--r-- | drivers/net/wireless/ath5k/hw.c | 82 |
1 files changed, 59 insertions, 23 deletions
diff --git a/drivers/net/wireless/ath5k/hw.c b/drivers/net/wireless/ath5k/hw.c index 3937e46e473..ad1a5b422c8 100644 --- a/drivers/net/wireless/ath5k/hw.c +++ b/drivers/net/wireless/ath5k/hw.c | |||
@@ -1092,34 +1092,57 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode, | |||
1092 | data = 0; | 1092 | data = 0; |
1093 | 1093 | ||
1094 | /* | 1094 | /* |
1095 | * Enable calibration and wait until completion | 1095 | * Start automatic gain calibration |
1096 | * | ||
1097 | * During AGC calibration RX path is re-routed to | ||
1098 | * a signal detector so we don't receive anything. | ||
1099 | * | ||
1100 | * This method is used to calibrate some static offsets | ||
1101 | * used together with on-the fly I/Q calibration (the | ||
1102 | * one performed via ath5k_hw_phy_calibrate), that doesn't | ||
1103 | * interrupt rx path. | ||
1104 | * | ||
1105 | * If we are in a noisy environment AGC calibration may time | ||
1106 | * out. | ||
1096 | */ | 1107 | */ |
1097 | AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, | 1108 | AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, |
1098 | AR5K_PHY_AGCCTL_CAL); | 1109 | AR5K_PHY_AGCCTL_CAL); |
1099 | 1110 | ||
1111 | /* At the same time start I/Q calibration for QAM constellation | ||
1112 | * -no need for CCK- */ | ||
1113 | ah->ah_calibration = false; | ||
1114 | if (!(mode == AR5K_MODE_11B)) { | ||
1115 | ah->ah_calibration = true; | ||
1116 | AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, | ||
1117 | AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15); | ||
1118 | AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, | ||
1119 | AR5K_PHY_IQ_RUN); | ||
1120 | } | ||
1121 | |||
1122 | /* Wait for gain calibration to finish (we check for I/Q calibration | ||
1123 | * during ath5k_phy_calibrate) */ | ||
1100 | if (ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, | 1124 | if (ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, |
1101 | AR5K_PHY_AGCCTL_CAL, 0, false)) { | 1125 | AR5K_PHY_AGCCTL_CAL, 0, false)) { |
1102 | ATH5K_ERR(ah->ah_sc, "calibration timeout (%uMHz)\n", | 1126 | ATH5K_ERR(ah->ah_sc, "gain calibration timeout (%uMHz)\n", |
1103 | channel->center_freq); | 1127 | channel->center_freq); |
1104 | return -EAGAIN; | 1128 | return -EAGAIN; |
1105 | } | 1129 | } |
1106 | 1130 | ||
1131 | /* | ||
1132 | * Start noise floor calibration | ||
1133 | * | ||
1134 | * If we run NF calibration before AGC, it always times out. | ||
1135 | * Binary HAL starts NF and AGC calibration at the same time | ||
1136 | * and only waits for AGC to finish. I believe that's wrong because | ||
1137 | * during NF calibration, rx path is also routed to a detector, so if | ||
1138 | * it doesn't finish we won't have RX. | ||
1139 | * | ||
1140 | * XXX: Find an interval that's OK for all cards... | ||
1141 | */ | ||
1107 | ret = ath5k_hw_noise_floor_calibration(ah, channel->center_freq); | 1142 | ret = ath5k_hw_noise_floor_calibration(ah, channel->center_freq); |
1108 | if (ret) | 1143 | if (ret) |
1109 | return ret; | 1144 | return ret; |
1110 | 1145 | ||
1111 | ah->ah_calibration = false; | ||
1112 | |||
1113 | /* A and G modes can use QAM modulation which requires enabling | ||
1114 | * I and Q calibration. Don't bother in B mode. */ | ||
1115 | if (!(mode == AR5K_MODE_11B)) { | ||
1116 | ah->ah_calibration = true; | ||
1117 | AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, | ||
1118 | AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15); | ||
1119 | AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, | ||
1120 | AR5K_PHY_IQ_RUN); | ||
1121 | } | ||
1122 | |||
1123 | /* | 1146 | /* |
1124 | * Reset queues and start beacon timers at the end of the reset routine | 1147 | * Reset queues and start beacon timers at the end of the reset routine |
1125 | */ | 1148 | */ |
@@ -1247,7 +1270,7 @@ int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, | |||
1247 | bool set_chip, u16 sleep_duration) | 1270 | bool set_chip, u16 sleep_duration) |
1248 | { | 1271 | { |
1249 | unsigned int i; | 1272 | unsigned int i; |
1250 | u32 staid; | 1273 | u32 staid, data; |
1251 | 1274 | ||
1252 | ATH5K_TRACE(ah->ah_sc); | 1275 | ATH5K_TRACE(ah->ah_sc); |
1253 | staid = ath5k_hw_reg_read(ah, AR5K_STA_ID1); | 1276 | staid = ath5k_hw_reg_read(ah, AR5K_STA_ID1); |
@@ -1259,7 +1282,8 @@ int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, | |||
1259 | case AR5K_PM_NETWORK_SLEEP: | 1282 | case AR5K_PM_NETWORK_SLEEP: |
1260 | if (set_chip) | 1283 | if (set_chip) |
1261 | ath5k_hw_reg_write(ah, | 1284 | ath5k_hw_reg_write(ah, |
1262 | AR5K_SLEEP_CTL_SLE | sleep_duration, | 1285 | AR5K_SLEEP_CTL_SLE_ALLOW | |
1286 | sleep_duration, | ||
1263 | AR5K_SLEEP_CTL); | 1287 | AR5K_SLEEP_CTL); |
1264 | 1288 | ||
1265 | staid |= AR5K_STA_ID1_PWR_SV; | 1289 | staid |= AR5K_STA_ID1_PWR_SV; |
@@ -1274,13 +1298,24 @@ int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, | |||
1274 | break; | 1298 | break; |
1275 | 1299 | ||
1276 | case AR5K_PM_AWAKE: | 1300 | case AR5K_PM_AWAKE: |
1301 | |||
1302 | staid &= ~AR5K_STA_ID1_PWR_SV; | ||
1303 | |||
1277 | if (!set_chip) | 1304 | if (!set_chip) |
1278 | goto commit; | 1305 | goto commit; |
1279 | 1306 | ||
1280 | ath5k_hw_reg_write(ah, AR5K_SLEEP_CTL_SLE_WAKE, | 1307 | /* Preserve sleep duration */ |
1281 | AR5K_SLEEP_CTL); | 1308 | data = ath5k_hw_reg_read(ah, AR5K_SLEEP_CTL); |
1309 | if( data & 0xffc00000 ){ | ||
1310 | data = 0; | ||
1311 | } else { | ||
1312 | data = data & 0xfffcffff; | ||
1313 | } | ||
1314 | |||
1315 | ath5k_hw_reg_write(ah, data, AR5K_SLEEP_CTL); | ||
1316 | udelay(15); | ||
1282 | 1317 | ||
1283 | for (i = 5000; i > 0; i--) { | 1318 | for (i = 50; i > 0; i--) { |
1284 | /* Check if the chip did wake up */ | 1319 | /* Check if the chip did wake up */ |
1285 | if ((ath5k_hw_reg_read(ah, AR5K_PCICFG) & | 1320 | if ((ath5k_hw_reg_read(ah, AR5K_PCICFG) & |
1286 | AR5K_PCICFG_SPWR_DN) == 0) | 1321 | AR5K_PCICFG_SPWR_DN) == 0) |
@@ -1288,15 +1323,13 @@ int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, | |||
1288 | 1323 | ||
1289 | /* Wait a bit and retry */ | 1324 | /* Wait a bit and retry */ |
1290 | udelay(200); | 1325 | udelay(200); |
1291 | ath5k_hw_reg_write(ah, AR5K_SLEEP_CTL_SLE_WAKE, | 1326 | ath5k_hw_reg_write(ah, data, AR5K_SLEEP_CTL); |
1292 | AR5K_SLEEP_CTL); | ||
1293 | } | 1327 | } |
1294 | 1328 | ||
1295 | /* Fail if the chip didn't wake up */ | 1329 | /* Fail if the chip didn't wake up */ |
1296 | if (i <= 0) | 1330 | if (i <= 0) |
1297 | return -EIO; | 1331 | return -EIO; |
1298 | 1332 | ||
1299 | staid &= ~AR5K_STA_ID1_PWR_SV; | ||
1300 | break; | 1333 | break; |
1301 | 1334 | ||
1302 | default: | 1335 | default: |
@@ -1325,6 +1358,7 @@ void ath5k_hw_start_rx(struct ath5k_hw *ah) | |||
1325 | { | 1358 | { |
1326 | ATH5K_TRACE(ah->ah_sc); | 1359 | ATH5K_TRACE(ah->ah_sc); |
1327 | ath5k_hw_reg_write(ah, AR5K_CR_RXE, AR5K_CR); | 1360 | ath5k_hw_reg_write(ah, AR5K_CR_RXE, AR5K_CR); |
1361 | ath5k_hw_reg_read(ah, AR5K_CR); | ||
1328 | } | 1362 | } |
1329 | 1363 | ||
1330 | /* | 1364 | /* |
@@ -1411,6 +1445,7 @@ int ath5k_hw_tx_start(struct ath5k_hw *ah, unsigned int queue) | |||
1411 | } | 1445 | } |
1412 | /* Start queue */ | 1446 | /* Start queue */ |
1413 | ath5k_hw_reg_write(ah, tx_queue, AR5K_CR); | 1447 | ath5k_hw_reg_write(ah, tx_queue, AR5K_CR); |
1448 | ath5k_hw_reg_read(ah, AR5K_CR); | ||
1414 | } else { | 1449 | } else { |
1415 | /* Return if queue is disabled */ | 1450 | /* Return if queue is disabled */ |
1416 | if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXD, queue)) | 1451 | if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXD, queue)) |
@@ -1708,6 +1743,7 @@ enum ath5k_int ath5k_hw_set_intr(struct ath5k_hw *ah, enum ath5k_int new_mask) | |||
1708 | * (they will be re-enabled afterwards). | 1743 | * (they will be re-enabled afterwards). |
1709 | */ | 1744 | */ |
1710 | ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER); | 1745 | ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER); |
1746 | ath5k_hw_reg_read(ah, AR5K_IER); | ||
1711 | 1747 | ||
1712 | old_mask = ah->ah_imr; | 1748 | old_mask = ah->ah_imr; |
1713 | 1749 | ||
@@ -3511,7 +3547,7 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) | |||
3511 | if (tq->tqi_flags & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE) | 3547 | if (tq->tqi_flags & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE) |
3512 | AR5K_REG_ENABLE_BITS(ah, | 3548 | AR5K_REG_ENABLE_BITS(ah, |
3513 | AR5K_QUEUE_MISC(queue), | 3549 | AR5K_QUEUE_MISC(queue), |
3514 | AR5K_QCU_MISC_TXE); | 3550 | AR5K_QCU_MISC_RDY_VEOL_POLICY); |
3515 | } | 3551 | } |
3516 | 3552 | ||
3517 | if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE) | 3553 | if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE) |