2020-04-24 00:52:55

by Sergey Ryazanov

[permalink] [raw]
Subject: [PATCH 0/6] ath9k: AR9002 calibration fixes

Hello,

I am faced a situation where the AR9220 AGC is goes crazy in case of a
low Rx signal in a noisy environment. It seems like it turned off the
internal LNA, so low signal neighbours become completely inaccessable,
and RSSI of neighbours with a strong signal drops by around 30 dB.
Periodic NF calibration perfectly solves this situation. But the NF
calibration itself in some cases could be blocked by the ADCs & I/Q
calibrations forever. This series is an attempt to address these NF
calibration blocking issues.

The first patch disables ADC gain & offset calibrations for unsupported
channels. The second patch is a trivial code cleanup.

And the last four patches together prevent the NF calibration infinite
deferring. They limit the run time of individual calibration, allow
correct running of NF calibration in between ADCs & I/Q calibrations
and prevent missing of NF periodic calibrations.

Run tested with AR9220 based NIC.

Sergey Ryazanov (6):
ath9k: fix AR9002 ADC and NF calibrations
ath9k: remove needless NFCAL_PENDING flag setting
ath9k: do not miss longcal on AR9002
ath9k: interleaved NF calibration on AR9002
ath9k: invalidate all calibrations at once
ath9k: add calibration timeout for AR9002

drivers/net/wireless/ath/ath9k/ar9002_calib.c | 49 +++++++++++++++----
drivers/net/wireless/ath/ath9k/calib.c | 16 +++---
drivers/net/wireless/ath/ath9k/hw.h | 2 +
3 files changed, 51 insertions(+), 16 deletions(-)

--
2.24.1


2020-04-24 00:52:56

by Sergey Ryazanov

[permalink] [raw]
Subject: [PATCH 5/6] ath9k: invalidate all calibrations at once

Previously after the calibration validity period is over,
calibrations are invalidated in a one at time manner. So, for AR9002
family, which has three calibrations, the full recalibration interval
becomes 3 x ATH_RESTART_CALINTERVAL. And each next calibration will be
separated by the ATH_RESTART_CALINTERVAL time from a previous one.

It seems like it is better to do whole recalibration at once. Also, this
change makes the driver behaviour a little simpler. So, invalidate all
calibrations at once at the end of the calibration validity interval.

This change affects only AR9002 chips family, since the AR9003 utilize
only a single calibration.

Signed-off-by: Sergey Ryazanov <[email protected]>
---
drivers/net/wireless/ath/ath9k/calib.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c
index 695c779ae8cf..2ac3eefd3851 100644
--- a/drivers/net/wireless/ath/ath9k/calib.c
+++ b/drivers/net/wireless/ath/ath9k/calib.c
@@ -209,14 +209,17 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
return true;
}

- if (!(ah->supp_cals & currCal->calData->calType))
- return true;
+ currCal = ah->cal_list;
+ do {
+ ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n",
+ currCal->calData->calType,
+ ah->curchan->chan->center_freq);

- ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n",
- currCal->calData->calType, ah->curchan->chan->center_freq);
+ ah->caldata->CalValid &= ~currCal->calData->calType;
+ currCal->calState = CAL_WAITING;

- ah->caldata->CalValid &= ~currCal->calData->calType;
- currCal->calState = CAL_WAITING;
+ currCal = currCal->calNext;
+ } while (currCal != ah->cal_list);

return false;
}
--
2.24.1

2020-04-24 00:53:21

by Sergey Ryazanov

[permalink] [raw]
Subject: [PATCH 6/6] ath9k: add calibration timeout for AR9002

ADC & I/Q calibrations could take infinite time to comple, since they
depend on received frames. In particular the I/Q mismatch calibration
requires receiving of OFDM frames for completion. But in the 2.4GHz
band, a station could receive only CCK frames for a very long time.

And while we wait for the completion of one of the mentioned
calibrations, the NF calibration is blocked. Moreover, in some
environments, I/Q calibration is unable to complete until a correct
noise calibration will be performed due to AGC behaviour.

In order to avoid delaying NF calibration on forever, limit the maximum
duration of ADCs & I/Q calibrations. If the calibration is not completed
within the maximum time, it will be interrupted and a next calibration
will be performed. The code that selects the next calibration has been
reworked to the loop so incompleted calibration will be respinned later.

А maximum calibration time of 30 seconds was selected to give the
calibration enough time to complete and to not interfere with the long
(NF) calibration.

Run tested with AR9220.

Signed-off-by: Sergey Ryazanov <[email protected]>
---
drivers/net/wireless/ath/ath9k/ar9002_calib.c | 25 +++++++++++++++++--
drivers/net/wireless/ath/ath9k/calib.c | 1 +
drivers/net/wireless/ath/ath9k/hw.h | 1 +
3 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c
index 68188f500949..fd53b5f9e9b5 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c
@@ -19,6 +19,8 @@
#include "ar9002_phy.h"

#define AR9285_CLCAL_REDO_THRESH 1
+/* AGC & I/Q calibrations time limit, ms */
+#define AR9002_CAL_MAX_TIME 30000

enum ar9002_cal_types {
ADC_GAIN_CAL = BIT(0),
@@ -104,6 +106,14 @@ static bool ar9002_hw_per_calibration(struct ath_hw *ah,
} else {
ar9002_hw_setup_calibration(ah, currCal);
}
+ } else if (time_after(jiffies, ah->cal_start_time +
+ msecs_to_jiffies(AR9002_CAL_MAX_TIME))) {
+ REG_CLR_BIT(ah, AR_PHY_TIMING_CTRL4(0),
+ AR_PHY_TIMING_CTRL4_DO_CAL);
+ ath_dbg(ath9k_hw_common(ah), CALIBRATE,
+ "calibration timeout\n");
+ currCal->calState = CAL_WAITING; /* Try later */
+ iscaldone = true;
}
} else if (!(caldata->CalValid & currCal->calData->calType)) {
ath9k_hw_reset_calibration(ah, currCal);
@@ -679,8 +689,19 @@ static int ar9002_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan,
if (!ar9002_hw_per_calibration(ah, chan, rxchainmask, currCal))
return 0;

- ah->cal_list_curr = currCal = currCal->calNext;
- percal_pending = currCal->calState == CAL_WAITING;
+ /* Looking for next waiting calibration if any */
+ for (currCal = currCal->calNext; currCal != ah->cal_list_curr;
+ currCal = currCal->calNext) {
+ if (currCal->calState == CAL_WAITING)
+ break;
+ }
+ if (currCal->calState == CAL_WAITING) {
+ percal_pending = true;
+ ah->cal_list_curr = currCal;
+ } else {
+ percal_pending = false;
+ ah->cal_list_curr = ah->cal_list;
+ }
}

/* Do not start a next calibration if the longcal is in action */
diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c
index 2ac3eefd3851..0422a33395b7 100644
--- a/drivers/net/wireless/ath/ath9k/calib.c
+++ b/drivers/net/wireless/ath/ath9k/calib.c
@@ -176,6 +176,7 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah,

ath9k_hw_setup_calibration(ah, currCal);

+ ah->cal_start_time = jiffies;
currCal->calState = CAL_RUNNING;

for (i = 0; i < AR5416_MAX_CHAINS; i++) {
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index c99f3c77c823..023599e10dd5 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -834,6 +834,7 @@ struct ath_hw {

/* Calibration */
u32 supp_cals;
+ unsigned long cal_start_time;
struct ath9k_cal_list iq_caldata;
struct ath9k_cal_list adcgain_caldata;
struct ath9k_cal_list adcdc_caldata;
--
2.24.1