]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/net/wireless/ath/ath9k/calib.c
ath9k: add initial hardware support for ar9271
[linux-2.6.git] / drivers / net / wireless / ath / ath9k / calib.c
index d1bbb02af8de39e24aee4bcbe32ea6470b2a2f5b..26d87527acbd32f07613f6526fe086df651c6047 100644 (file)
@@ -753,6 +753,98 @@ static void ath9k_olc_temp_compensation(struct ath_hw *ah)
        }
 }
 
+static void ath9k_hw_9271_pa_cal(struct ath_hw *ah)
+{
+       u32 regVal;
+       unsigned int i;
+       u32 regList [][2] = {
+               { 0x786c, 0 },
+               { 0x7854, 0 },
+               { 0x7820, 0 },
+               { 0x7824, 0 },
+               { 0x7868, 0 },
+               { 0x783c, 0 },
+               { 0x7838, 0 } ,
+               { 0x7828, 0 } ,
+       };
+
+       for (i = 0; i < ARRAY_SIZE(regList); i++)
+               regList[i][1] = REG_READ(ah, regList[i][0]);
+
+       regVal = REG_READ(ah, 0x7834);
+       regVal &= (~(0x1));
+       REG_WRITE(ah, 0x7834, regVal);
+       regVal = REG_READ(ah, 0x9808);
+       regVal |= (0x1 << 27);
+       REG_WRITE(ah, 0x9808, regVal);
+
+       /* 786c,b23,1, pwddac=1 */
+       REG_RMW_FIELD(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC, 1);
+       /* 7854, b5,1, pdrxtxbb=1 */
+       REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1, 1);
+       /* 7854, b7,1, pdv2i=1 */
+       REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I, 1);
+       /* 7854, b8,1, pddacinterface=1 */
+       REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF, 1);
+       /* 7824,b12,0, offcal=0 */
+       REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0);
+       /* 7838, b1,0, pwddb=0 */
+       REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0);
+       /* 7820,b11,0, enpacal=0 */
+       REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0);
+       /* 7820,b25,1, pdpadrv1=0 */
+       REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 0);
+       /* 7820,b24,0, pdpadrv2=0 */
+       REG_RMW_FIELD(ah, AR9285_AN_RF2G1,AR9285_AN_RF2G1_PDPADRV2,0);
+       /* 7820,b23,0, pdpaout=0 */
+       REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0);
+       /* 783c,b14-16,7, padrvgn2tab_0=7 */
+       REG_RMW_FIELD(ah, AR9285_AN_RF2G8,AR9285_AN_RF2G8_PADRVGN2TAB0, 7);
+       /*
+        * 7838,b29-31,0, padrvgn1tab_0=0
+        * does not matter since we turn it off
+        */
+       REG_RMW_FIELD(ah, AR9285_AN_RF2G7,AR9285_AN_RF2G7_PADRVGN2TAB0, 0);
+
+       REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_CCOMP, 0xfff);
+
+       /* Set:
+        * localmode=1,bmode=1,bmoderxtx=1,synthon=1,
+        * txon=1,paon=1,oscon=1,synthon_force=1
+        */
+       REG_WRITE(ah, AR9285_AN_TOP2, 0xca0358a0);
+       udelay(30);
+       REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9271_AN_RF2G6_OFFS, 0);
+
+       /* find off_6_1; */
+       for (i = 6; i >= 0; i--) {
+               regVal = REG_READ(ah, 0x7834);
+               regVal |= (1 << (20 + i));
+               REG_WRITE(ah, 0x7834, regVal);
+               udelay(1);
+               //regVal = REG_READ(ah, 0x7834);
+               regVal &= (~(0x1 << (20 + i)));
+               regVal |= (MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9)
+                           << (20 + i));
+               REG_WRITE(ah, 0x7834, regVal);
+       }
+
+       /*  Empirical offset correction  */
+#if 0
+       REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9271_AN_RF2G6_OFFS, 0x20);
+#endif
+
+       regVal = REG_READ(ah, 0x7834);
+       regVal |= 0x1;
+       REG_WRITE(ah, 0x7834, regVal);
+       regVal = REG_READ(ah, 0x9808);
+       regVal &= (~(0x1 << 27));
+       REG_WRITE(ah, 0x9808, regVal);
+
+       for (i = 0; i < ARRAY_SIZE(regList); i++)
+               REG_WRITE(ah, regList[i][0], regList[i][1]);
+}
+
 static inline void ath9k_hw_9285_pa_cal(struct ath_hw *ah)
 {
 
@@ -869,14 +961,26 @@ bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan,
                }
        }
 
+       /* Do NF cal only at longer intervals */
        if (longcal) {
-               if (AR_SREV_9285_11_OR_LATER(ah))
+               /* Do periodic PAOffset Cal */
+               if (AR_SREV_9271(ah))
+                       ath9k_hw_9271_pa_cal(ah);
+               else if (AR_SREV_9285_11_OR_LATER(ah))
                        ath9k_hw_9285_pa_cal(ah);
 
                if (OLC_FOR_AR9280_20_LATER || OLC_FOR_AR9287_10_LATER)
                        ath9k_olc_temp_compensation(ah);
+
+               /* Get the value from the previous NF cal and update history buffer */
                ath9k_hw_getnf(ah, chan);
+
+               /*
+                * Load the NF from history buffer of the current channel.
+                * NF is slow time-variant, so it is OK to use a historical value.
+                */
                ath9k_hw_loadnf(ah, ah->curchan);
+
                ath9k_hw_start_nfcal(ah);
        }