diff options
author | Felix Fietkau <nbd@openwrt.org> | 2014-10-25 11:19:30 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-10-27 14:16:18 -0400 |
commit | 7b8aaead958e38b1eb8944c5f9c90ce066500268 (patch) | |
tree | 1854b2009b0af5f7e5fca49313861cc2fb626c02 /drivers/net/wireless/ath | |
parent | 70e535ed00290ed415ccfb200695ea30b6af51e5 (diff) |
ath9k: restart hardware after noise floor calibration failure
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>
Diffstat (limited to 'drivers/net/wireless/ath')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ar9002_calib.c | 11 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ar9003_calib.c | 11 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/calib.c | 6 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/calib.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/debug.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/debug.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/hw-ops.h | 7 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/hw.h | 6 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/link.c | 12 |
9 files changed, 33 insertions, 24 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c index cdc74005650c..6bfdebfcdeef 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c | |||
@@ -657,14 +657,13 @@ static void ar9002_hw_olc_temp_compensation(struct ath_hw *ah) | |||
657 | ar9280_hw_olc_temp_compensation(ah); | 657 | ar9280_hw_olc_temp_compensation(ah); |
658 | } | 658 | } |
659 | 659 | ||
660 | static bool ar9002_hw_calibrate(struct ath_hw *ah, | 660 | static int ar9002_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, |
661 | struct ath9k_channel *chan, | 661 | u8 rxchainmask, bool longcal) |
662 | u8 rxchainmask, | ||
663 | bool longcal) | ||
664 | { | 662 | { |
665 | bool iscaldone = true; | 663 | bool iscaldone = true; |
666 | struct ath9k_cal_list *currCal = ah->cal_list_curr; | 664 | struct ath9k_cal_list *currCal = ah->cal_list_curr; |
667 | bool nfcal, nfcal_pending = false; | 665 | bool nfcal, nfcal_pending = false; |
666 | int ret; | ||
668 | 667 | ||
669 | nfcal = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF); | 668 | nfcal = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF); |
670 | if (ah->caldata) | 669 | if (ah->caldata) |
@@ -698,7 +697,9 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah, | |||
698 | * NF is slow time-variant, so it is OK to use a | 697 | * NF is slow time-variant, so it is OK to use a |
699 | * historical value. | 698 | * historical value. |
700 | */ | 699 | */ |
701 | ath9k_hw_loadnf(ah, ah->curchan); | 700 | ret = ath9k_hw_loadnf(ah, ah->curchan); |
701 | if (ret < 0) | ||
702 | return ret; | ||
702 | } | 703 | } |
703 | 704 | ||
704 | if (longcal) { | 705 | if (longcal) { |
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index ac8301ef5242..06ab71db6e80 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c | |||
@@ -121,13 +121,12 @@ static bool ar9003_hw_per_calibration(struct ath_hw *ah, | |||
121 | return iscaldone; | 121 | return iscaldone; |
122 | } | 122 | } |
123 | 123 | ||
124 | static bool ar9003_hw_calibrate(struct ath_hw *ah, | 124 | static int ar9003_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, |
125 | struct ath9k_channel *chan, | 125 | u8 rxchainmask, bool longcal) |
126 | u8 rxchainmask, | ||
127 | bool longcal) | ||
128 | { | 126 | { |
129 | bool iscaldone = true; | 127 | bool iscaldone = true; |
130 | struct ath9k_cal_list *currCal = ah->cal_list_curr; | 128 | struct ath9k_cal_list *currCal = ah->cal_list_curr; |
129 | int ret; | ||
131 | 130 | ||
132 | /* | 131 | /* |
133 | * For given calibration: | 132 | * For given calibration: |
@@ -163,7 +162,9 @@ static bool ar9003_hw_calibrate(struct ath_hw *ah, | |||
163 | * NF is slow time-variant, so it is OK to use a historical | 162 | * NF is slow time-variant, so it is OK to use a historical |
164 | * value. | 163 | * value. |
165 | */ | 164 | */ |
166 | ath9k_hw_loadnf(ah, ah->curchan); | 165 | ret = ath9k_hw_loadnf(ah, ah->curchan); |
166 | if (ret < 0) | ||
167 | return ret; | ||
167 | 168 | ||
168 | /* start NF calibration, without updating BB NF register */ | 169 | /* start NF calibration, without updating BB NF register */ |
169 | ath9k_hw_start_nfcal(ah, false); | 170 | ath9k_hw_start_nfcal(ah, false); |
diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 278365b8a895..e200a6e3aca5 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c | |||
@@ -234,7 +234,7 @@ void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update) | |||
234 | REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); | 234 | REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); |
235 | } | 235 | } |
236 | 236 | ||
237 | void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) | 237 | int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) |
238 | { | 238 | { |
239 | struct ath9k_nfcal_hist *h = NULL; | 239 | struct ath9k_nfcal_hist *h = NULL; |
240 | unsigned i, j; | 240 | unsigned i, j; |
@@ -301,7 +301,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) | |||
301 | ath_dbg(common, ANY, | 301 | ath_dbg(common, ANY, |
302 | "Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n", | 302 | "Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n", |
303 | REG_READ(ah, AR_PHY_AGC_CONTROL)); | 303 | REG_READ(ah, AR_PHY_AGC_CONTROL)); |
304 | return; | 304 | return -ETIMEDOUT; |
305 | } | 305 | } |
306 | 306 | ||
307 | /* | 307 | /* |
@@ -322,6 +322,8 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) | |||
322 | } | 322 | } |
323 | } | 323 | } |
324 | REGWRITE_BUFFER_FLUSH(ah); | 324 | REGWRITE_BUFFER_FLUSH(ah); |
325 | |||
326 | return 0; | ||
325 | } | 327 | } |
326 | 328 | ||
327 | 329 | ||
diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index b8ed95e9a335..87badf4bb8a4 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h | |||
@@ -109,7 +109,7 @@ struct ath9k_pacal_info{ | |||
109 | 109 | ||
110 | bool ath9k_hw_reset_calvalid(struct ath_hw *ah); | 110 | bool ath9k_hw_reset_calvalid(struct ath_hw *ah); |
111 | void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update); | 111 | void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update); |
112 | void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan); | 112 | int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan); |
113 | bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan); | 113 | bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan); |
114 | void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, | 114 | void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, |
115 | struct ath9k_channel *chan); | 115 | struct ath9k_channel *chan); |
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index cf4a98b98cf3..2a2a17df5fb3 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c | |||
@@ -863,6 +863,7 @@ static ssize_t read_file_reset(struct file *file, char __user *user_buf, | |||
863 | [RESET_TYPE_MAC_HANG] = "MAC Hang", | 863 | [RESET_TYPE_MAC_HANG] = "MAC Hang", |
864 | [RESET_TYPE_BEACON_STUCK] = "Stuck Beacon", | 864 | [RESET_TYPE_BEACON_STUCK] = "Stuck Beacon", |
865 | [RESET_TYPE_MCI] = "MCI Reset", | 865 | [RESET_TYPE_MCI] = "MCI Reset", |
866 | [RESET_TYPE_CALIBRATION] = "Calibration error", | ||
866 | }; | 867 | }; |
867 | char buf[512]; | 868 | char buf[512]; |
868 | unsigned int len = 0; | 869 | unsigned int len = 0; |
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 53ae15bd0c9d..bd75b1f716db 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h | |||
@@ -49,6 +49,7 @@ enum ath_reset_type { | |||
49 | RESET_TYPE_MAC_HANG, | 49 | RESET_TYPE_MAC_HANG, |
50 | RESET_TYPE_BEACON_STUCK, | 50 | RESET_TYPE_BEACON_STUCK, |
51 | RESET_TYPE_MCI, | 51 | RESET_TYPE_MCI, |
52 | RESET_TYPE_CALIBRATION, | ||
52 | __RESET_TYPE_MAX | 53 | __RESET_TYPE_MAX |
53 | }; | 54 | }; |
54 | 55 | ||
diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index 8e85efeaeffc..88769b64b20b 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h | |||
@@ -41,10 +41,9 @@ static inline void ath9k_hw_set_desc_link(struct ath_hw *ah, void *ds, | |||
41 | ath9k_hw_ops(ah)->set_desc_link(ds, link); | 41 | ath9k_hw_ops(ah)->set_desc_link(ds, link); |
42 | } | 42 | } |
43 | 43 | ||
44 | static inline bool ath9k_hw_calibrate(struct ath_hw *ah, | 44 | static inline int ath9k_hw_calibrate(struct ath_hw *ah, |
45 | struct ath9k_channel *chan, | 45 | struct ath9k_channel *chan, |
46 | u8 rxchainmask, | 46 | u8 rxchainmask, bool longcal) |
47 | bool longcal) | ||
48 | { | 47 | { |
49 | return ath9k_hw_ops(ah)->calibrate(ah, chan, rxchainmask, longcal); | 48 | return ath9k_hw_ops(ah)->calibrate(ah, chan, rxchainmask, longcal); |
50 | } | 49 | } |
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 7a81f5b864c7..f204099c38b8 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h | |||
@@ -688,10 +688,8 @@ struct ath_hw_ops { | |||
688 | bool power_off); | 688 | bool power_off); |
689 | void (*rx_enable)(struct ath_hw *ah); | 689 | void (*rx_enable)(struct ath_hw *ah); |
690 | void (*set_desc_link)(void *ds, u32 link); | 690 | void (*set_desc_link)(void *ds, u32 link); |
691 | bool (*calibrate)(struct ath_hw *ah, | 691 | int (*calibrate)(struct ath_hw *ah, struct ath9k_channel *chan, |
692 | struct ath9k_channel *chan, | 692 | u8 rxchainmask, bool longcal); |
693 | u8 rxchainmask, | ||
694 | bool longcal); | ||
695 | bool (*get_isr)(struct ath_hw *ah, enum ath9k_int *masked, | 693 | bool (*get_isr)(struct ath_hw *ah, enum ath9k_int *masked, |
696 | u32 *sync_cause_p); | 694 | u32 *sync_cause_p); |
697 | void (*set_txdesc)(struct ath_hw *ah, void *ds, | 695 | void (*set_txdesc)(struct ath_hw *ah, void *ds, |
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 2343f56e6498..b829263e3d0a 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c | |||
@@ -371,9 +371,15 @@ void ath_ani_calibrate(unsigned long data) | |||
371 | 371 | ||
372 | /* Perform calibration if necessary */ | 372 | /* Perform calibration if necessary */ |
373 | if (longcal || shortcal) { | 373 | if (longcal || shortcal) { |
374 | common->ani.caldone = | 374 | int ret = ath9k_hw_calibrate(ah, ah->curchan, ah->rxchainmask, |
375 | ath9k_hw_calibrate(ah, ah->curchan, | 375 | longcal); |
376 | ah->rxchainmask, longcal); | 376 | if (ret < 0) { |
377 | common->ani.caldone = 0; | ||
378 | ath9k_queue_reset(sc, RESET_TYPE_CALIBRATION); | ||
379 | return; | ||
380 | } | ||
381 | |||
382 | common->ani.caldone = ret; | ||
377 | } | 383 | } |
378 | 384 | ||
379 | ath_dbg(common, ANI, | 385 | ath_dbg(common, ANI, |