aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath5k
diff options
context:
space:
mode:
authorBruno Randolf <br1@einfach.org>2010-03-09 02:56:05 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-03-10 16:16:56 -0500
commit86415d43efd4f7093979cfa8a80232114266f1a4 (patch)
tree47af91f1bccde944f86a77b8fdb586e1877c9af7 /drivers/net/wireless/ath/ath5k
parenta3b980fd1391e75068ae25f3817728b27bfdb04c (diff)
ath5k: fix I/Q calibration (for real)
I/Q calibration was completely broken, resulting in a high number of CRC errors on received packets. before i could see around 10% to 20% CRC errors, with this patch they are between 0% and 3%. 1.) the removal of the mask in commit "ath5k: Fix I/Q calibration (f1cf2dbd0f798b71b1590e7aca6647f2caef1649)" resulted in no mask beeing used when writing the I/Q values into the register. additional errors in the calculation of the values (see 2.) resulted too high numbers, exceeding the masks, so wrong values like 0xfffffffe were written. to be safe we should always use the bitmask when writing parts of a register. 2.) using a (s32) cast for q_coff is a wrong conversion to signed, since we convert to a signed value later by substracting 128. this resulted in too low numbers for Q many times, which were limited to -16 by the boundary check later on. 3.) checked everything against the HAL sources and took over comments and minor optimizations from there. 4.) we can't use ENABLE_BITS when we want to write a number (the number can contain zeros). also always write the correction values first and set ENABLE bit last, like the HAL does. Signed-off-by: Bruno Randolf <br1@einfach.org> Cc: stable@kernel.org Acked-by: Nick Kossifidis <mickflemm@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath/ath5k')
-rw-r--r--drivers/net/wireless/ath/ath5k/phy.c37
-rw-r--r--drivers/net/wireless/ath/ath5k/reg.h1
2 files changed, 20 insertions, 18 deletions
diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c
index ffe253ab9be7..eff3323efb4b 100644
--- a/drivers/net/wireless/ath/ath5k/phy.c
+++ b/drivers/net/wireless/ath/ath5k/phy.c
@@ -1386,38 +1386,39 @@ static int ath5k_hw_rf511x_calibrate(struct ath5k_hw *ah,
1386 goto done; 1386 goto done;
1387 1387
1388 /* Calibration has finished, get the results and re-run */ 1388 /* Calibration has finished, get the results and re-run */
1389
1390 /* work around empty results which can apparently happen on 5212 */
1389 for (i = 0; i <= 10; i++) { 1391 for (i = 0; i <= 10; i++) {
1390 iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR); 1392 iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR);
1391 i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I); 1393 i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I);
1392 q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q); 1394 q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q);
1395 ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
1396 "iq_corr:%x i_pwr:%x q_pwr:%x", iq_corr, i_pwr, q_pwr);
1397 if (i_pwr && q_pwr)
1398 break;
1393 } 1399 }
1394 1400
1395 i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7; 1401 i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7;
1396 q_coffd = q_pwr >> 7; 1402 q_coffd = q_pwr >> 7;
1397 1403
1398 /* No correction */ 1404 /* protect against divide by 0 and loss of sign bits */
1399 if (i_coffd == 0 || q_coffd == 0) 1405 if (i_coffd == 0 || q_coffd < 2)
1400 goto done; 1406 goto done;
1401 1407
1402 i_coff = ((-iq_corr) / i_coffd); 1408 i_coff = (-iq_corr) / i_coffd;
1409 i_coff = clamp(i_coff, -32, 31); /* signed 6 bit */
1403 1410
1404 /* Boundary check */ 1411 q_coff = (i_pwr / q_coffd) - 128;
1405 if (i_coff > 31) 1412 q_coff = clamp(q_coff, -16, 15); /* signed 5 bit */
1406 i_coff = 31;
1407 if (i_coff < -32)
1408 i_coff = -32;
1409 1413
1410 q_coff = (((s32)i_pwr / q_coffd) - 128); 1414 ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
1415 "new I:%d Q:%d (i_coffd:%x q_coffd:%x)",
1416 i_coff, q_coff, i_coffd, q_coffd);
1411 1417
1412 /* Boundary check */ 1418 /* Commit new I/Q values (set enable bit last to match HAL sources) */
1413 if (q_coff > 15) 1419 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_I_COFF, i_coff);
1414 q_coff = 15; 1420 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_Q_COFF, q_coff);
1415 if (q_coff < -16) 1421 AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE);
1416 q_coff = -16;
1417
1418 /* Commit new I/Q value */
1419 AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE |
1420 ((u32)q_coff) | ((u32)i_coff << AR5K_PHY_IQ_CORR_Q_I_COFF_S));
1421 1422
1422 /* Re-enable calibration -if we don't we'll commit 1423 /* Re-enable calibration -if we don't we'll commit
1423 * the same values again and again */ 1424 * the same values again and again */
diff --git a/drivers/net/wireless/ath/ath5k/reg.h b/drivers/net/wireless/ath/ath5k/reg.h
index 4cb9c5df9f46..1464f89b249c 100644
--- a/drivers/net/wireless/ath/ath5k/reg.h
+++ b/drivers/net/wireless/ath/ath5k/reg.h
@@ -2187,6 +2187,7 @@
2187 */ 2187 */
2188#define AR5K_PHY_IQ 0x9920 /* Register Address */ 2188#define AR5K_PHY_IQ 0x9920 /* Register Address */
2189#define AR5K_PHY_IQ_CORR_Q_Q_COFF 0x0000001f /* Mask for q correction info */ 2189#define AR5K_PHY_IQ_CORR_Q_Q_COFF 0x0000001f /* Mask for q correction info */
2190#define AR5K_PHY_IQ_CORR_Q_Q_COFF_S 0
2190#define AR5K_PHY_IQ_CORR_Q_I_COFF 0x000007e0 /* Mask for i correction info */ 2191#define AR5K_PHY_IQ_CORR_Q_I_COFF 0x000007e0 /* Mask for i correction info */
2191#define AR5K_PHY_IQ_CORR_Q_I_COFF_S 5 2192#define AR5K_PHY_IQ_CORR_Q_I_COFF_S 5
2192#define AR5K_PHY_IQ_CORR_ENABLE 0x00000800 /* Enable i/q correction */ 2193#define AR5K_PHY_IQ_CORR_ENABLE 0x00000800 /* Enable i/q correction */