lkml.org 
[lkml]   [2010]   [Mar]   [30]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[013/156] ath5k: fix I/Q calibration (for real)
    2.6.33-stable review patch.  If anyone has any objections, please let us know.

    ------------------

    From: Bruno Randolf <br1@einfach.org>

    commit 86415d43efd4f7093979cfa8a80232114266f1a4 upstream.

    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>
    Acked-by: Nick Kossifidis <mickflemm@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

    ---
    drivers/net/wireless/ath/ath5k/phy.c | 41 +++++++++++++++++------------------
    drivers/net/wireless/ath/ath5k/reg.h | 1
    2 files changed, 22 insertions(+), 20 deletions(-)

    --- 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(str
    goto done;

    /* Calibration has finished, get the results and re-run */
    +
    + /* work around empty results which can apparently happen on 5212 */
    for (i = 0; i <= 10; i++) {
    iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR);
    i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I);
    q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q);
    + ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
    + "iq_corr:%x i_pwr:%x q_pwr:%x", iq_corr, i_pwr, q_pwr);
    + if (i_pwr && q_pwr)
    + break;
    }

    i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7;
    q_coffd = q_pwr >> 7;

    - /* No correction */
    - if (i_coffd == 0 || q_coffd == 0)
    + /* protect against divide by 0 and loss of sign bits */
    + if (i_coffd == 0 || q_coffd < 2)
    goto done;

    - i_coff = ((-iq_corr) / i_coffd);
    + i_coff = (-iq_corr) / i_coffd;
    + i_coff = clamp(i_coff, -32, 31); /* signed 6 bit */
    +
    + q_coff = (i_pwr / q_coffd) - 128;
    + q_coff = clamp(q_coff, -16, 15); /* signed 5 bit */

    - /* Boundary check */
    - if (i_coff > 31)
    - i_coff = 31;
    - if (i_coff < -32)
    - i_coff = -32;
    -
    - q_coff = (((s32)i_pwr / q_coffd) - 128);
    -
    - /* Boundary check */
    - if (q_coff > 15)
    - q_coff = 15;
    - if (q_coff < -16)
    - q_coff = -16;
    -
    - /* Commit new I/Q value */
    - AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE |
    - ((u32)q_coff) | ((u32)i_coff << AR5K_PHY_IQ_CORR_Q_I_COFF_S));
    + ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
    + "new I:%d Q:%d (i_coffd:%x q_coffd:%x)",
    + i_coff, q_coff, i_coffd, q_coffd);
    +
    + /* Commit new I/Q values (set enable bit last to match HAL sources) */
    + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_I_COFF, i_coff);
    + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_Q_COFF, q_coff);
    + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE);

    /* Re-enable calibration -if we don't we'll commit
    * the same values again and again */
    --- a/drivers/net/wireless/ath/ath5k/reg.h
    +++ b/drivers/net/wireless/ath/ath5k/reg.h
    @@ -2187,6 +2187,7 @@
    */
    #define AR5K_PHY_IQ 0x9920 /* Register Address */
    #define AR5K_PHY_IQ_CORR_Q_Q_COFF 0x0000001f /* Mask for q correction info */
    +#define AR5K_PHY_IQ_CORR_Q_Q_COFF_S 0
    #define AR5K_PHY_IQ_CORR_Q_I_COFF 0x000007e0 /* Mask for i correction info */
    #define AR5K_PHY_IQ_CORR_Q_I_COFF_S 5
    #define AR5K_PHY_IQ_CORR_ENABLE 0x00000800 /* Enable i/q correction */



    \
     
     \ /
      Last update: 2010-03-31 01:57    [W:11.833 / U:0.020 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site