Commit 587f7031 authored by Michal Kazior's avatar Michal Kazior Committed by Kalle Valo

ath10k: handle cycle counter wraparound

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 active time
value. It is, however, impossible to determine the
point at which Rx Clear Count has been divided by
two so it is not reported upon wraparound.

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 improve hostapd ACS a little bit.
Reported-by: default avatarSrinivasa Duvvuri <sduvvuri@chromium.org>
Signed-off-by: default avatarMichal Kazior <michal.kazior@tieto.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 0936ea3f
...@@ -48,6 +48,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -48,6 +48,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.name = "qca988x hw2.0", .name = "qca988x hw2.0",
.patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR, .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR,
.uart_pin = 7, .uart_pin = 7,
.has_shifted_cc_wraparound = true,
.fw = { .fw = {
.dir = QCA988X_HW_2_0_FW_DIR, .dir = QCA988X_HW_2_0_FW_DIR,
.fw = QCA988X_HW_2_0_FW_FILE, .fw = QCA988X_HW_2_0_FW_FILE,
......
...@@ -577,6 +577,13 @@ struct ath10k { ...@@ -577,6 +577,13 @@ struct ath10k {
u32 patch_load_addr; u32 patch_load_addr;
int uart_pin; 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 { struct ath10k_hw_params_fw {
const char *dir; const char *dir;
const char *fw; const char *fw;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
#include <linux/types.h> #include <linux/types.h>
#include "core.h"
#include "hw.h" #include "hw.h"
const struct ath10k_hw_regs qca988x_regs = { const struct ath10k_hw_regs qca988x_regs = {
...@@ -56,3 +57,23 @@ const struct ath10k_hw_regs qca6174_regs = { ...@@ -56,3 +57,23 @@ const struct ath10k_hw_regs qca6174_regs = {
.soc_chip_id_address = 0x000f0, .soc_chip_id_address = 0x000f0,
.scratch_3_address = 0x0028, .scratch_3_address = 0x0028,
}; };
void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey,
u32 cc, u32 rcc, u32 cc_prev, u32 rcc_prev)
{
u32 cc_fix = 0;
survey->filled |= SURVEY_INFO_TIME |
SURVEY_INFO_TIME_BUSY;
if (ar->hw_params.has_shifted_cc_wraparound && cc < cc_prev) {
cc_fix = 0x7fffffff;
survey->filled &= ~SURVEY_INFO_TIME_BUSY;
}
cc -= cc_prev - cc_fix;
rcc -= rcc_prev;
survey->time = CCNT_TO_MSEC(cc);
survey->time_busy = CCNT_TO_MSEC(rcc);
}
...@@ -169,6 +169,9 @@ struct ath10k_hw_regs { ...@@ -169,6 +169,9 @@ struct ath10k_hw_regs {
extern const struct ath10k_hw_regs qca988x_regs; extern const struct ath10k_hw_regs qca988x_regs;
extern const struct ath10k_hw_regs qca6174_regs; extern const struct ath10k_hw_regs qca6174_regs;
void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey,
u32 cc, u32 rcc, u32 cc_prev, u32 rcc_prev);
#define QCA_REV_988X(ar) ((ar)->hw_rev == ATH10K_HW_QCA988X) #define QCA_REV_988X(ar) ((ar)->hw_rev == ATH10K_HW_QCA988X)
#define QCA_REV_6174(ar) ((ar)->hw_rev == ATH10K_HW_QCA6174) #define QCA_REV_6174(ar) ((ar)->hw_rev == ATH10K_HW_QCA6174)
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "testmode.h" #include "testmode.h"
#include "wmi-ops.h" #include "wmi-ops.h"
#include "p2p.h" #include "p2p.h"
#include "hw.h"
/* MAIN WMI cmd track */ /* MAIN WMI cmd track */
static struct wmi_cmd_map wmi_cmd_map = { static struct wmi_cmd_map wmi_cmd_map = {
...@@ -1640,16 +1641,16 @@ void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) ...@@ -1640,16 +1641,16 @@ void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
* visited channel. The reported cycle count is global * visited channel. The reported cycle count is global
* and per-channel cycle count must be calculated */ * and per-channel cycle count must be calculated */
cycle_count -= ar->survey_last_cycle_count;
rx_clear_count -= ar->survey_last_rx_clear_count;
survey = &ar->survey[idx]; survey = &ar->survey[idx];
survey->time = CCNT_TO_MSEC(cycle_count);
survey->time_busy = CCNT_TO_MSEC(rx_clear_count);
survey->noise = noise_floor; survey->noise = noise_floor;
survey->filled = SURVEY_INFO_TIME | survey->filled = SURVEY_INFO_NOISE_DBM;
SURVEY_INFO_TIME_BUSY |
SURVEY_INFO_NOISE_DBM; ath10k_hw_fill_survey_time(ar,
survey,
cycle_count,
rx_clear_count,
ar->survey_last_cycle_count,
ar->survey_last_rx_clear_count);
} }
ar->survey_last_rx_clear_count = rx_clear_count; ar->survey_last_rx_clear_count = rx_clear_count;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment