[PATCH v3 1/2] ath10k: handle cycle counter wraparound

Michal Kazior michal.kazior at tieto.com
Fri May 22 01:18:12 PDT 2015


When QCA988X cycle counter HW register wraps
around it resets to 0x7fffffff instead of 0. All
other cycle counter related registers are divided
by 2 so they never wraparound themselves. QCA61X4
has a uniform CC and it wraparounds in a regular
fashion though.

Worst case wraparound time is approx 24 seconds
(2**31 / 88MHz). Since scan channel visit times
are max 5 seconds (offchannel case) it is
guaranteed there's been at most 1 wraparound and
it is possible to compute survey data.

This fixes some occasional incorrect survey data
on QCA988X as some channels (depending on how/when
scan/offchannel requests were requested) would
have approx 24 sec active time which wasn't
actually the case.

This should help make hostapd ACS more reliable.

Reported-by: Srinivasa Duvvuri <sduvvuri at chromium.org>
Signed-off-by: Michal Kazior <michal.kazior at tieto.com>
---

Notes:
    v3:
     * split into 2 separate patches
     * don't ignore all wraparound data: scans can be computed
     * apply the shifted wraparound fix only for QCA988X
     * improve commit log

 drivers/net/wireless/ath/ath10k/core.c | 15 +++++++++++++++
 drivers/net/wireless/ath/ath10k/core.h | 12 ++++++++++++
 drivers/net/wireless/ath/ath10k/wmi.c  | 14 ++++++++++++--
 3 files changed, 39 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index bcccae19325d..0cc2122d6921 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -48,6 +48,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
 		.name = "qca988x hw2.0",
 		.patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR,
 		.uart_pin = 7,
+		.has_shifted_cc_wraparound = true,
 		.fw = {
 			.dir = QCA988X_HW_2_0_FW_DIR,
 			.fw = QCA988X_HW_2_0_FW_FILE,
@@ -102,6 +103,20 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
 	},
 };
 
+void ath10k_core_get_cc_delta(struct ath10k *ar,
+			      u32 *cc_delta, u32 *rcc_delta,
+			      u32 cc, u32 rcc,
+			      u32 cc_prev, u32 rcc_prev)
+{
+	if (ar->hw_params.has_shifted_cc_wraparound && cc < cc_prev) {
+		cc_prev -= 0x7fffffff;
+		rcc *= 2;
+	}
+
+	*cc_delta = cc - cc_prev;
+	*rcc_delta = rcc - rcc_prev;
+}
+
 static void ath10k_send_suspend_complete(struct ath10k *ar)
 {
 	ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot suspend complete\n");
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 827b3d79ed0c..fb403972b1c5 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -572,6 +572,13 @@ struct ath10k {
 		u32 patch_load_addr;
 		int uart_pin;
 
+		/* This is true if given HW chip has a quirky Cycle Counter
+		 * wraparound which resets to 0x7fffffff instead of 0. All
+		 * other CC related counters (e.g. Rx Clear Count) are divided
+		 * by 2 so they never wraparound themselves.
+		 */
+		bool has_shifted_cc_wraparound;
+
 		struct ath10k_hw_params_fw {
 			const char *dir;
 			const char *fw;
@@ -742,4 +749,9 @@ void ath10k_core_stop(struct ath10k *ar);
 int ath10k_core_register(struct ath10k *ar, u32 chip_id);
 void ath10k_core_unregister(struct ath10k *ar);
 
+void ath10k_core_get_cc_delta(struct ath10k *ar,
+			      u32 *cc_delta, u32 *rcc_delta,
+			      u32 cc, u32 rcc,
+			      u32 cc_prev, u32 rcc_prev);
+
 #endif /* _CORE_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 0fabe689179c..52ed48b7e5f9 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -1640,8 +1640,18 @@ void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
 		 * visited channel. The reported cycle count is global
 		 * and per-channel cycle count must be calculated */
 
-		cycle_count -= ar->survey_last_cycle_count;
-		rx_clear_count -= ar->survey_last_rx_clear_count;
+		/* Worst case wraparound time is approx 24 seconds (2**31 /
+		 * 88MHz). Since scan channel visit times are max 5 seconds
+		 * (offchannel case) it is guaranteed there's been at most 1
+		 * wraparound and it is possible to compute survey data.
+		 */
+		ath10k_core_get_cc_delta(ar,
+					 &cycle_count,
+					 &rx_clear_count,
+					 cycle_count,
+					 rx_clear_count,
+					 ar->survey_last_cycle_count,
+					 ar->survey_last_rx_clear_count);
 
 		survey = &ar->survey[idx];
 		survey->time = WMI_CHAN_INFO_MSEC(cycle_count);
-- 
2.1.4




More information about the ath10k mailing list