[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