ath9k: restart hardware after noise floor calibration failure
authorFelix Fietkau <nbd@openwrt.org>
Sat, 25 Oct 2014 15:19:30 +0000 (17:19 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 27 Oct 2014 18:16:18 +0000 (14:16 -0400)
When NF calibration fails, the radio often becomes deaf. The usual
hardware hang checks do not detect this, so it's better to issue a reset
when that happens.

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/ar9002_calib.c
drivers/net/wireless/ath/ath9k/ar9003_calib.c
drivers/net/wireless/ath/ath9k/calib.c
drivers/net/wireless/ath/ath9k/calib.h
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/debug.h
drivers/net/wireless/ath/ath9k/hw-ops.h
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/link.c

index cdc74005650ce9848147ee0e051c64f9df989ffd..6bfdebfcdeef043d55dc36503972fb247fc2366c 100644 (file)
@@ -657,14 +657,13 @@ static void ar9002_hw_olc_temp_compensation(struct ath_hw *ah)
                ar9280_hw_olc_temp_compensation(ah);
 }
 
-static bool ar9002_hw_calibrate(struct ath_hw *ah,
-                               struct ath9k_channel *chan,
-                               u8 rxchainmask,
-                               bool longcal)
+static int ar9002_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan,
+                              u8 rxchainmask, bool longcal)
 {
        bool iscaldone = true;
        struct ath9k_cal_list *currCal = ah->cal_list_curr;
        bool nfcal, nfcal_pending = false;
+       int ret;
 
        nfcal = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF);
        if (ah->caldata)
@@ -698,7 +697,9 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah,
                         * NF is slow time-variant, so it is OK to use a
                         * historical value.
                         */
-                       ath9k_hw_loadnf(ah, ah->curchan);
+                       ret = ath9k_hw_loadnf(ah, ah->curchan);
+                       if (ret < 0)
+                               return ret;
                }
 
                if (longcal) {
index ac8301ef52420a00009f4d78b82e700987ea7508..06ab71db6e804b76a867cc036cb27887a2ac5b62 100644 (file)
@@ -121,13 +121,12 @@ static bool ar9003_hw_per_calibration(struct ath_hw *ah,
        return iscaldone;
 }
 
-static bool ar9003_hw_calibrate(struct ath_hw *ah,
-                               struct ath9k_channel *chan,
-                               u8 rxchainmask,
-                               bool longcal)
+static int ar9003_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan,
+                              u8 rxchainmask, bool longcal)
 {
        bool iscaldone = true;
        struct ath9k_cal_list *currCal = ah->cal_list_curr;
+       int ret;
 
        /*
         * For given calibration:
@@ -163,7 +162,9 @@ static bool ar9003_hw_calibrate(struct ath_hw *ah,
                 * NF is slow time-variant, so it is OK to use a historical
                 * value.
                 */
-               ath9k_hw_loadnf(ah, ah->curchan);
+               ret = ath9k_hw_loadnf(ah, ah->curchan);
+               if (ret < 0)
+                       return ret;
 
                /* start NF calibration, without updating BB NF register */
                ath9k_hw_start_nfcal(ah, false);
index 278365b8a8955dda362292b99949e63155be354c..e200a6e3aca5f4e4ce98354814358724cb0444d3 100644 (file)
@@ -234,7 +234,7 @@ void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update)
        REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
 }
 
-void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
+int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
 {
        struct ath9k_nfcal_hist *h = NULL;
        unsigned i, j;
@@ -301,7 +301,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
                ath_dbg(common, ANY,
                        "Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n",
                        REG_READ(ah, AR_PHY_AGC_CONTROL));
-               return;
+               return -ETIMEDOUT;
        }
 
        /*
@@ -322,6 +322,8 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
                }
        }
        REGWRITE_BUFFER_FLUSH(ah);
+
+       return 0;
 }
 
 
index b8ed95e9a335d90d86e8d0dd4031e5abc2e8dffc..87badf4bb8a4109bfeb9bdda6454fff2a0b2e630 100644 (file)
@@ -109,7 +109,7 @@ struct ath9k_pacal_info{
 
 bool ath9k_hw_reset_calvalid(struct ath_hw *ah);
 void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update);
-void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan);
+int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan);
 bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan);
 void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
                                  struct ath9k_channel *chan);
index cf4a98b98cf31e39d77feb2043ab65cbea19ba6b..2a2a17df5fb3ae39270ab828abc595b443c42b56 100644 (file)
@@ -863,6 +863,7 @@ static ssize_t read_file_reset(struct file *file, char __user *user_buf,
                [RESET_TYPE_MAC_HANG] = "MAC Hang",
                [RESET_TYPE_BEACON_STUCK] = "Stuck Beacon",
                [RESET_TYPE_MCI] = "MCI Reset",
+               [RESET_TYPE_CALIBRATION] = "Calibration error",
        };
        char buf[512];
        unsigned int len = 0;
index 53ae15bd0c9d4ac8b40a7a9de3689c607d56c3cd..bd75b1f716db527208afc525a0fd186d527e6e29 100644 (file)
@@ -49,6 +49,7 @@ enum ath_reset_type {
        RESET_TYPE_MAC_HANG,
        RESET_TYPE_BEACON_STUCK,
        RESET_TYPE_MCI,
+       RESET_TYPE_CALIBRATION,
        __RESET_TYPE_MAX
 };
 
index 8e85efeaeffcf67d08dcc9e94d793f5bb0559a9f..88769b64b20b29d9f9cb71c6ffbca27cc3d314d4 100644 (file)
@@ -41,10 +41,9 @@ static inline void ath9k_hw_set_desc_link(struct ath_hw *ah, void *ds,
        ath9k_hw_ops(ah)->set_desc_link(ds, link);
 }
 
-static inline bool ath9k_hw_calibrate(struct ath_hw *ah,
-                                     struct ath9k_channel *chan,
-                                     u8 rxchainmask,
-                                     bool longcal)
+static inline int ath9k_hw_calibrate(struct ath_hw *ah,
+                                    struct ath9k_channel *chan,
+                                    u8 rxchainmask, bool longcal)
 {
        return ath9k_hw_ops(ah)->calibrate(ah, chan, rxchainmask, longcal);
 }
index 7a81f5b864c73b0f0982243eb87c94b8195f576b..f204099c38b822137c45b79b9880237bbce7420d 100644 (file)
@@ -688,10 +688,8 @@ struct ath_hw_ops {
                                     bool power_off);
        void (*rx_enable)(struct ath_hw *ah);
        void (*set_desc_link)(void *ds, u32 link);
-       bool (*calibrate)(struct ath_hw *ah,
-                         struct ath9k_channel *chan,
-                         u8 rxchainmask,
-                         bool longcal);
+       int (*calibrate)(struct ath_hw *ah, struct ath9k_channel *chan,
+                        u8 rxchainmask, bool longcal);
        bool (*get_isr)(struct ath_hw *ah, enum ath9k_int *masked,
                        u32 *sync_cause_p);
        void (*set_txdesc)(struct ath_hw *ah, void *ds,
index 2343f56e64987730bcb3ad1cb5542c41dcd94310..b829263e3d0a7f2a849bb96bfeed4ab948a0c1bc 100644 (file)
@@ -371,9 +371,15 @@ void ath_ani_calibrate(unsigned long data)
 
        /* Perform calibration if necessary */
        if (longcal || shortcal) {
-               common->ani.caldone =
-                       ath9k_hw_calibrate(ah, ah->curchan,
-                                          ah->rxchainmask, longcal);
+               int ret = ath9k_hw_calibrate(ah, ah->curchan, ah->rxchainmask,
+                                            longcal);
+               if (ret < 0) {
+                       common->ani.caldone = 0;
+                       ath9k_queue_reset(sc, RESET_TYPE_CALIBRATION);
+                       return;
+               }
+
+               common->ani.caldone = ret;
        }
 
        ath_dbg(common, ANI,