From 583a6629432ca95813a585a7117331ffe36fe939 Mon Sep 17 00:00:00 2001 From: Ryan Hsu Date: Wed, 8 Mar 2017 13:52:04 +0200 Subject: [PATCH 01/24] ath10k: improve the firmware download time for QCA6174 Len Brown reported the system resume time is taking more than 2 seconds in bug - https://bugzilla.kernel.org/show_bug.cgi?id=185621. The reason of the 2 seconds is due to the firmware download time. The chip is booted up in the default reference clock speed to handle the firmware download to chip memory and advanced to the support higher speed clock to run the firmware after all. The default reference clock in the hardware is slow so that the firmware download time is taking up to 2 seconds for a 600KB firmware file. [76796.349701] ath10k_pci : boot uploading firmware image len 688691 [76798.334612] ath10k_pci : htt tx max num pending tx 1056 The resolution here is to enable the higher speed clock if the hardware supported before the firmware download at BMI stage, so that the hardware can handle the firmare download in a more efficient way. This can help to improve the firmware download time from 2 seconds to around 500ms for the same 600KB firmware file. [322858.577919] ath10k_pci boot uploading firmware image len 688691 [322859.093094] ath10k_pci htt tx max num pending tx 1056 The steps to advance to the higher speed clock is very hardware specific, so adding the hardware ops for the hardware that can support this. Reported-by: Len Brown Tested-by: Paul Menzel Signed-off-by: Ryan Hsu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/bmi.c | 72 +++++++ drivers/net/wireless/ath/ath10k/bmi.h | 2 + drivers/net/wireless/ath/ath10k/core.c | 4 +- drivers/net/wireless/ath/ath10k/hw.c | 265 +++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/hw.h | 69 +++++++ 5 files changed, 411 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index 2872d347ea78..abeee200310b 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -19,12 +19,21 @@ #include "hif.h" #include "debug.h" #include "htc.h" +#include "hw.h" void ath10k_bmi_start(struct ath10k *ar) { + int ret; + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi start\n"); ar->bmi.done_sent = false; + + /* Enable hardware clock to speed up firmware download */ + if (ar->hw_params.hw_ops->enable_pll_clk) { + ret = ar->hw_params.hw_ops->enable_pll_clk(ar); + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi enable pll ret %d\n", ret); + } } int ath10k_bmi_done(struct ath10k *ar) @@ -129,6 +138,69 @@ int ath10k_bmi_read_memory(struct ath10k *ar, return 0; } +int ath10k_bmi_write_soc_reg(struct ath10k *ar, u32 address, u32 reg_val) +{ + struct bmi_cmd cmd; + u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.write_soc_reg); + int ret; + + ath10k_dbg(ar, ATH10K_DBG_BMI, + "bmi write soc register 0x%08x val 0x%08x\n", + address, reg_val); + + if (ar->bmi.done_sent) { + ath10k_warn(ar, "bmi write soc register command in progress\n"); + return -EBUSY; + } + + cmd.id = __cpu_to_le32(BMI_WRITE_SOC_REGISTER); + cmd.write_soc_reg.addr = __cpu_to_le32(address); + cmd.write_soc_reg.value = __cpu_to_le32(reg_val); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL); + if (ret) { + ath10k_warn(ar, "Unable to write soc register to device: %d\n", + ret); + return ret; + } + + return 0; +} + +int ath10k_bmi_read_soc_reg(struct ath10k *ar, u32 address, u32 *reg_val) +{ + struct bmi_cmd cmd; + union bmi_resp resp; + u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_soc_reg); + u32 resplen = sizeof(resp.read_soc_reg); + int ret; + + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register 0x%08x\n", + address); + + if (ar->bmi.done_sent) { + ath10k_warn(ar, "bmi read soc register command in progress\n"); + return -EBUSY; + } + + cmd.id = __cpu_to_le32(BMI_READ_SOC_REGISTER); + cmd.read_soc_reg.addr = __cpu_to_le32(address); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen); + if (ret) { + ath10k_warn(ar, "Unable to read soc register from device: %d\n", + ret); + return ret; + } + + *reg_val = __le32_to_cpu(resp.read_soc_reg.value); + + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register value 0x%08x\n", + *reg_val); + + return 0; +} + int ath10k_bmi_write_memory(struct ath10k *ar, u32 address, const void *buffer, u32 length) { diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h index 7d3231acfb24..a65f26267fe3 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.h +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -232,4 +232,6 @@ int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address); int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length); int ath10k_bmi_fast_download(struct ath10k *ar, u32 address, const void *buffer, u32 length); +int ath10k_bmi_read_soc_reg(struct ath10k *ar, u32 address, u32 *reg_val); +int ath10k_bmi_write_soc_reg(struct ath10k *ar, u32 address, u32 reg_val); #endif /* _BMI_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 0a8e29e9a0eb..9916c428d02c 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -166,7 +166,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, - .hw_ops = &qca988x_ops, + .hw_ops = &qca6174_ops, + .hw_clk = qca6174_clk, + .target_cpu_freq = 176000000, .decap_align_bytes = 4, }, { diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c index 33fb26833cd0..85582bdd7524 100644 --- a/drivers/net/wireless/ath/ath10k/hw.c +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -19,6 +19,7 @@ #include "hw.h" #include "hif.h" #include "wmi-ops.h" +#include "bmi.h" const struct ath10k_hw_regs qca988x_regs = { .rtc_soc_base_address = 0x00004000, @@ -72,6 +73,9 @@ const struct ath10k_hw_regs qca6174_regs = { .pcie_intr_fw_mask = 0x00000400, .pcie_intr_ce_mask_all = 0x0007f800, .pcie_intr_clr_address = 0x00000014, + .cpu_pll_init_address = 0x00404020, + .cpu_speed_address = 0x00404024, + .core_clk_div_address = 0x00404028, }; const struct ath10k_hw_regs qca99x0_regs = { @@ -187,6 +191,73 @@ const struct ath10k_hw_values qca4019_values = { .ce_desc_meta_data_lsb = 4, }; +const struct ath10k_hw_clk_params qca6174_clk[ATH10K_HW_REFCLK_COUNT] = { + { + .refclk = 48000000, + .div = 0xe, + .rnfrac = 0x2aaa8, + .settle_time = 2400, + .refdiv = 0, + .outdiv = 1, + }, + { + .refclk = 19200000, + .div = 0x24, + .rnfrac = 0x2aaa8, + .settle_time = 960, + .refdiv = 0, + .outdiv = 1, + }, + { + .refclk = 24000000, + .div = 0x1d, + .rnfrac = 0x15551, + .settle_time = 1200, + .refdiv = 0, + .outdiv = 1, + }, + { + .refclk = 26000000, + .div = 0x1b, + .rnfrac = 0x4ec4, + .settle_time = 1300, + .refdiv = 0, + .outdiv = 1, + }, + { + .refclk = 37400000, + .div = 0x12, + .rnfrac = 0x34b49, + .settle_time = 1870, + .refdiv = 0, + .outdiv = 1, + }, + { + .refclk = 38400000, + .div = 0x12, + .rnfrac = 0x15551, + .settle_time = 1920, + .refdiv = 0, + .outdiv = 1, + }, + { + .refclk = 40000000, + .div = 0x12, + .rnfrac = 0x26665, + .settle_time = 2000, + .refdiv = 0, + .outdiv = 1, + }, + { + .refclk = 52000000, + .div = 0x1b, + .rnfrac = 0x4ec4, + .settle_time = 2600, + .refdiv = 0, + .outdiv = 1, + }, +}; + void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey, u32 cc, u32 rcc, u32 cc_prev, u32 rcc_prev) { @@ -361,6 +432,195 @@ static void ath10k_hw_qca988x_set_coverage_class(struct ath10k *ar, mutex_unlock(&ar->conf_mutex); } +/** + * ath10k_hw_qca6174_enable_pll_clock() - enable the qca6174 hw pll clock + * @ar: the ath10k blob + * + * This function is very hardware specific, the clock initialization + * steps is very sensitive and could lead to unknown crash, so they + * should be done in sequence. + * + * *** Be aware if you planned to refactor them. *** + * + * Return: 0 if successfully enable the pll, otherwise EINVAL + */ +static int ath10k_hw_qca6174_enable_pll_clock(struct ath10k *ar) +{ + int ret, wait_limit; + u32 clk_div_addr, pll_init_addr, speed_addr; + u32 addr, reg_val, mem_val; + struct ath10k_hw_params *hw; + const struct ath10k_hw_clk_params *hw_clk; + + hw = &ar->hw_params; + + if (ar->regs->core_clk_div_address == 0 || + ar->regs->cpu_pll_init_address == 0 || + ar->regs->cpu_speed_address == 0) + return -EINVAL; + + clk_div_addr = ar->regs->core_clk_div_address; + pll_init_addr = ar->regs->cpu_pll_init_address; + speed_addr = ar->regs->cpu_speed_address; + + /* Read efuse register to find out the right hw clock configuration */ + addr = (RTC_SOC_BASE_ADDRESS | EFUSE_OFFSET); + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + /* sanitize if the hw refclk index is out of the boundary */ + if (MS(reg_val, EFUSE_XTAL_SEL) > ATH10K_HW_REFCLK_COUNT) + return -EINVAL; + + hw_clk = &hw->hw_clk[MS(reg_val, EFUSE_XTAL_SEL)]; + + /* Set the rnfrac and outdiv params to bb_pll register */ + addr = (RTC_SOC_BASE_ADDRESS | BB_PLL_CONFIG_OFFSET); + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + reg_val &= ~(BB_PLL_CONFIG_FRAC_MASK | BB_PLL_CONFIG_OUTDIV_MASK); + reg_val |= (SM(hw_clk->rnfrac, BB_PLL_CONFIG_FRAC) | + SM(hw_clk->outdiv, BB_PLL_CONFIG_OUTDIV)); + ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); + if (ret) + return -EINVAL; + + /* Set the correct settle time value to pll_settle register */ + addr = (RTC_WMAC_BASE_ADDRESS | WLAN_PLL_SETTLE_OFFSET); + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + reg_val &= ~WLAN_PLL_SETTLE_TIME_MASK; + reg_val |= SM(hw_clk->settle_time, WLAN_PLL_SETTLE_TIME); + ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); + if (ret) + return -EINVAL; + + /* Set the clock_ctrl div to core_clk_ctrl register */ + addr = (RTC_SOC_BASE_ADDRESS | SOC_CORE_CLK_CTRL_OFFSET); + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + reg_val &= ~SOC_CORE_CLK_CTRL_DIV_MASK; + reg_val |= SM(1, SOC_CORE_CLK_CTRL_DIV); + ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); + if (ret) + return -EINVAL; + + /* Set the clock_div register */ + mem_val = 1; + ret = ath10k_bmi_write_memory(ar, clk_div_addr, &mem_val, + sizeof(mem_val)); + if (ret) + return -EINVAL; + + /* Configure the pll_control register */ + addr = (RTC_WMAC_BASE_ADDRESS | WLAN_PLL_CONTROL_OFFSET); + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + reg_val |= (SM(hw_clk->refdiv, WLAN_PLL_CONTROL_REFDIV) | + SM(hw_clk->div, WLAN_PLL_CONTROL_DIV) | + SM(1, WLAN_PLL_CONTROL_NOPWD)); + ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); + if (ret) + return -EINVAL; + + /* busy wait (max 1s) the rtc_sync status register indicate ready */ + wait_limit = 100000; + addr = (RTC_WMAC_BASE_ADDRESS | RTC_SYNC_STATUS_OFFSET); + do { + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + if (!MS(reg_val, RTC_SYNC_STATUS_PLL_CHANGING)) + break; + + wait_limit--; + udelay(10); + + } while (wait_limit > 0); + + if (MS(reg_val, RTC_SYNC_STATUS_PLL_CHANGING)) + return -EINVAL; + + /* Unset the pll_bypass in pll_control register */ + addr = (RTC_WMAC_BASE_ADDRESS | WLAN_PLL_CONTROL_OFFSET); + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + reg_val &= ~WLAN_PLL_CONTROL_BYPASS_MASK; + reg_val |= SM(0, WLAN_PLL_CONTROL_BYPASS); + ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); + if (ret) + return -EINVAL; + + /* busy wait (max 1s) the rtc_sync status register indicate ready */ + wait_limit = 100000; + addr = (RTC_WMAC_BASE_ADDRESS | RTC_SYNC_STATUS_OFFSET); + do { + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + if (!MS(reg_val, RTC_SYNC_STATUS_PLL_CHANGING)) + break; + + wait_limit--; + udelay(10); + + } while (wait_limit > 0); + + if (MS(reg_val, RTC_SYNC_STATUS_PLL_CHANGING)) + return -EINVAL; + + /* Enable the hardware cpu clock register */ + addr = (RTC_SOC_BASE_ADDRESS | SOC_CPU_CLOCK_OFFSET); + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + reg_val &= ~SOC_CPU_CLOCK_STANDARD_MASK; + reg_val |= SM(1, SOC_CPU_CLOCK_STANDARD); + ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); + if (ret) + return -EINVAL; + + /* unset the nopwd from pll_control register */ + addr = (RTC_WMAC_BASE_ADDRESS | WLAN_PLL_CONTROL_OFFSET); + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + reg_val &= ~WLAN_PLL_CONTROL_NOPWD_MASK; + ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); + if (ret) + return -EINVAL; + + /* enable the pll_init register */ + mem_val = 1; + ret = ath10k_bmi_write_memory(ar, pll_init_addr, &mem_val, + sizeof(mem_val)); + if (ret) + return -EINVAL; + + /* set the target clock frequency to speed register */ + ret = ath10k_bmi_write_memory(ar, speed_addr, &hw->target_cpu_freq, + sizeof(hw->target_cpu_freq)); + if (ret) + return -EINVAL; + + return 0; +} + const struct ath10k_hw_ops qca988x_ops = { .set_coverage_class = ath10k_hw_qca988x_set_coverage_class, }; @@ -374,3 +634,8 @@ static int ath10k_qca99x0_rx_desc_get_l3_pad_bytes(struct htt_rx_desc *rxd) const struct ath10k_hw_ops qca99x0_ops = { .rx_desc_get_l3_pad_bytes = ath10k_qca99x0_rx_desc_get_l3_pad_bytes, }; + +const struct ath10k_hw_ops qca6174_ops = { + .set_coverage_class = ath10k_hw_qca988x_set_coverage_class, + .enable_pll_clk = ath10k_hw_qca6174_enable_pll_clock, +}; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index f0fda0f2b3b4..d370b573e0f9 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -255,6 +255,9 @@ struct ath10k_hw_regs { u32 pcie_intr_fw_mask; u32 pcie_intr_ce_mask_all; u32 pcie_intr_clr_address; + u32 cpu_pll_init_address; + u32 cpu_speed_address; + u32 core_clk_div_address; }; extern const struct ath10k_hw_regs qca988x_regs; @@ -363,6 +366,30 @@ enum ath10k_hw_cc_wraparound_type { ATH10K_HW_CC_WRAP_SHIFTED_EACH = 2, }; +enum ath10k_hw_refclk_speed { + ATH10K_HW_REFCLK_UNKNOWN = -1, + ATH10K_HW_REFCLK_48_MHZ = 0, + ATH10K_HW_REFCLK_19_2_MHZ = 1, + ATH10K_HW_REFCLK_24_MHZ = 2, + ATH10K_HW_REFCLK_26_MHZ = 3, + ATH10K_HW_REFCLK_37_4_MHZ = 4, + ATH10K_HW_REFCLK_38_4_MHZ = 5, + ATH10K_HW_REFCLK_40_MHZ = 6, + ATH10K_HW_REFCLK_52_MHZ = 7, + + /* must be the last one */ + ATH10K_HW_REFCLK_COUNT, +}; + +struct ath10k_hw_clk_params { + u32 refclk; + u32 div; + u32 rnfrac; + u32 settle_time; + u32 refdiv; + u32 outdiv; +}; + struct ath10k_hw_params { u32 id; u16 dev_id; @@ -416,6 +443,10 @@ struct ath10k_hw_params { /* Number of bytes used for alignment in rx_hdr_status of rx desc. */ int decap_align_bytes; + + /* hw specific clock control parameters */ + const struct ath10k_hw_clk_params *hw_clk; + int target_cpu_freq; }; struct htt_rx_desc; @@ -424,10 +455,14 @@ struct htt_rx_desc; struct ath10k_hw_ops { int (*rx_desc_get_l3_pad_bytes)(struct htt_rx_desc *rxd); void (*set_coverage_class)(struct ath10k *ar, s16 value); + int (*enable_pll_clk)(struct ath10k *ar); }; extern const struct ath10k_hw_ops qca988x_ops; extern const struct ath10k_hw_ops qca99x0_ops; +extern const struct ath10k_hw_ops qca6174_ops; + +extern const struct ath10k_hw_clk_params qca6174_clk[]; static inline int ath10k_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw, @@ -847,4 +882,38 @@ ath10k_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw, #define WAVE1_PHYCLK_USEC_MASK 0x0000007F #define WAVE1_PHYCLK_USEC_LSB 0 +/* qca6174 PLL offset/mask */ +#define SOC_CORE_CLK_CTRL_OFFSET 0x00000114 +#define SOC_CORE_CLK_CTRL_DIV_LSB 0 +#define SOC_CORE_CLK_CTRL_DIV_MASK 0x00000007 + +#define EFUSE_OFFSET 0x0000032c +#define EFUSE_XTAL_SEL_LSB 8 +#define EFUSE_XTAL_SEL_MASK 0x00000700 + +#define BB_PLL_CONFIG_OFFSET 0x000002f4 +#define BB_PLL_CONFIG_FRAC_LSB 0 +#define BB_PLL_CONFIG_FRAC_MASK 0x0003ffff +#define BB_PLL_CONFIG_OUTDIV_LSB 18 +#define BB_PLL_CONFIG_OUTDIV_MASK 0x001c0000 + +#define WLAN_PLL_SETTLE_OFFSET 0x0018 +#define WLAN_PLL_SETTLE_TIME_LSB 0 +#define WLAN_PLL_SETTLE_TIME_MASK 0x000007ff + +#define WLAN_PLL_CONTROL_OFFSET 0x0014 +#define WLAN_PLL_CONTROL_DIV_LSB 0 +#define WLAN_PLL_CONTROL_DIV_MASK 0x000003ff +#define WLAN_PLL_CONTROL_REFDIV_LSB 10 +#define WLAN_PLL_CONTROL_REFDIV_MASK 0x00003c00 +#define WLAN_PLL_CONTROL_BYPASS_LSB 16 +#define WLAN_PLL_CONTROL_BYPASS_MASK 0x00010000 +#define WLAN_PLL_CONTROL_NOPWD_LSB 18 +#define WLAN_PLL_CONTROL_NOPWD_MASK 0x00040000 + +#define RTC_SYNC_STATUS_OFFSET 0x0244 +#define RTC_SYNC_STATUS_PLL_CHANGING_LSB 5 +#define RTC_SYNC_STATUS_PLL_CHANGING_MASK 0x00000020 +/* qca6174 PLL offset/mask end */ + #endif /* _HW_H_ */ From 912b6e8850a51a09ec771aedf2b4428ac9b34e20 Mon Sep 17 00:00:00 2001 From: Ryan Hsu Date: Wed, 8 Mar 2017 13:52:05 +0200 Subject: [PATCH 02/24] ath10k: improve the firmware download time for QCA9377 QCA9377 is the family of QCA61x4 which shared the same procedure to enable the hardware clock that could improve the firmware download time. Signed-off-by: Ryan Hsu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 9916c428d02c..f450ebbb28d5 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -282,7 +282,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA9377_BOARD_DATA_SZ, .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ, }, - .hw_ops = &qca988x_ops, + .hw_ops = &qca6174_ops, + .hw_clk = qca6174_clk, + .target_cpu_freq = 176000000, .decap_align_bytes = 4, }, { From 16ff1fb0e32f76a5d285a6f23b82d21aa52813c6 Mon Sep 17 00:00:00 2001 From: Dmitry Tunin Date: Wed, 8 Mar 2017 13:52:07 +0200 Subject: [PATCH 03/24] ath9k_htc: Add support of AirTies 1eda:2315 AR9271 device T: Bus=01 Lev=02 Prnt=02 Port=02 Cnt=01 Dev#= 7 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=ff(vend.) Sub=ff Prot=ff MxPS=64 #Cfgs= 1 P: Vendor=1eda ProdID=2315 Rev=01.08 S: Manufacturer=ATHEROS S: Product=USB2.0 WLAN S: SerialNumber=12345 C: #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 6 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) Signed-off-by: Dmitry Tunin Cc: stable@vger.kernel.org Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/hif_usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index de2d212f39ec..05dd056cab6e 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -37,6 +37,7 @@ static struct usb_device_id ath9k_hif_usb_ids[] = { { USB_DEVICE(0x0cf3, 0xb002) }, /* Ubiquiti WifiStation */ { USB_DEVICE(0x057c, 0x8403) }, /* AVM FRITZ!WLAN 11N v2 USB */ { USB_DEVICE(0x0471, 0x209e) }, /* Philips (or NXP) PTA01 */ + { USB_DEVICE(0x1eda, 0x2315) }, /* AirTies */ { USB_DEVICE(0x0cf3, 0x7015), .driver_info = AR9287_USB }, /* Atheros */ From c73f8c00330f59ce9b1ace9ff698aca83390d358 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Wed, 8 Mar 2017 13:52:06 +0200 Subject: [PATCH 04/24] ath10k: fix a warning during channel switch with multiple vaps Doing a channel switch via hostapd_cli seems to update the new channel context for each VAP's appropriately as below in 'ath10k_mac_update_vif_chan', hence we can safely suppress the warning that shows up during this operation and dump the warning only if no vaps are available for channel switch hostapd_cli -i wlan0 chan_switch 5 5200 OK ath10k_pci : mac chanctx switch n_vifs 3 mode 1 ath10k_pci : mac chanctx switch vdev_id 2 freq 5180->5200 width 0->0 ath10k_pci : mac chanctx switch vdev_id 1 freq 5180->5200 width 0->0 ath10k_pci : mac chanctx switch vdev_id 0 freq 5180->5200 width 0->0 Call Trace: WARNING: backports-20161201-3.14.77-9ab3068/drivers/net/wireless/ath/ath10k/mac.c:7126 [] (warn_slowpath_null) from [] (ath10k_reconfig_complete+0xe4/0x25c [ath10k_core]) [] (ath10k_reconfig_complete [ath10k_core]) [] (ath10k_mac_vif_ap_csa_work+0x214/0x370 [ath10k_core]) [] (ath10k_mac_op_change_chanctx+0x108/0x128 [ath10k_core]) [] (ieee80211_recalc_chanctx_min_def+0x30c/0x430 [mac80211]) [] (ieee80211_recalc_smps_chanctx+0x2ec/0x840 [mac80211]) [] (ieee80211_vif_use_reserved_context+0x7c/0xf8 [mac80211]) [] (ieee80211_vif_use_reserved_context [mac80211]) [] (ieee80211_csa_finalize_work+0x5c/0x88 [mac80211]) Fixes: d7bf4b4aba05 ("ath10k: fix ar->rx_channel updating logic") Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 3029f257a19a..f0e46804ad82 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -7129,7 +7129,7 @@ ath10k_mac_update_rx_channel(struct ath10k *ar, lockdep_assert_held(&ar->data_lock); WARN_ON(ctx && vifs); - WARN_ON(vifs && n_vifs != 1); + WARN_ON(vifs && !n_vifs); /* FIXME: Sort of an optimization and a workaround. Peers and vifs are * on a linked list now. Doing a lookup peer -> vif -> chanctx for each From 4aa2d31f5df8c47cc92ccacf49a2dff3db6c1d53 Mon Sep 17 00:00:00 2001 From: Christophe Jaillet Date: Wed, 8 Mar 2017 13:52:09 +0200 Subject: [PATCH 05/24] wcn36xx: Fix error handling Reorder 'out_free_dxe_pool' and 'out_free_dxe_ctl' error handling labels in order to match the way resources have been allocated. Signed-off-by: Christophe JAILLET Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wcn36xx/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 7a0c2e7da7f6..2b05154d05a4 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -337,10 +337,10 @@ static int wcn36xx_start(struct ieee80211_hw *hw) wcn36xx_smd_stop(wcn); out_free_smd_buf: kfree(wcn->hal_buf); -out_free_dxe_pool: - wcn36xx_dxe_free_mem_pools(wcn); out_free_dxe_ctl: wcn36xx_dxe_free_ctl_blks(wcn); +out_free_dxe_pool: + wcn36xx_dxe_free_mem_pools(wcn); out_smd_close: wcn36xx_smd_close(wcn); out_err: From f2de576dcfd094b02297c251223b0e2c9de96c33 Mon Sep 17 00:00:00 2001 From: Hamad Kadmany Date: Wed, 8 Mar 2017 13:52:10 +0200 Subject: [PATCH 06/24] wil6210: set dma mask to reflect device capability device supports 48bit addresses, reflect that by setting the dma mask accordingly. Signed-off-by: Hamad Kadmany Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/pcie_bus.c | 16 ++++++++++++++++ drivers/net/wireless/ath/wil6210/pmc.c | 17 ++++++++++++++++- drivers/net/wireless/ath/wil6210/txrx.c | 19 ++++++++++++++++++- drivers/net/wireless/ath/wil6210/wil6210.h | 1 + 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 874c787727fe..b38515fc7ce7 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -211,6 +211,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev_err(dev, "wil_if_alloc failed: %d\n", rc); return rc; } + wil->pdev = pdev; pci_set_drvdata(pdev, wil); /* rollback to if_free */ @@ -224,6 +225,21 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) } /* rollback to err_plat */ + /* device supports 48bit addresses */ + rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); + if (rc) { + dev_err(dev, "dma_set_mask_and_coherent(48) failed: %d\n", rc); + rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (rc) { + dev_err(dev, + "dma_set_mask_and_coherent(32) failed: %d\n", + rc); + goto err_plat; + } + } else { + wil->use_extended_dma_addr = 1; + } + rc = pci_enable_device(pdev); if (rc) { wil_err(wil, diff --git a/drivers/net/wireless/ath/wil6210/pmc.c b/drivers/net/wireless/ath/wil6210/pmc.c index 3ff4f4ce9fef..b067fdf086d4 100644 --- a/drivers/net/wireless/ath/wil6210/pmc.c +++ b/drivers/net/wireless/ath/wil6210/pmc.c @@ -107,13 +107,28 @@ void wil_pmc_alloc(struct wil6210_priv *wil, /* Allocate pring buffer and descriptors. * vring->va should be aligned on its size rounded up to power of 2 - * This is granted by the dma_alloc_coherent + * This is granted by the dma_alloc_coherent. + * + * HW has limitation that all vrings addresses must share the same + * upper 16 msb bits part of 48 bits address. To workaround that, + * if we are using 48 bit addresses switch to 32 bit allocation + * before allocating vring memory. + * + * There's no check for the return value of dma_set_mask_and_coherent, + * since we assume if we were able to set the mask during + * initialization in this system it will not fail if we set it again */ + if (wil->use_extended_dma_addr) + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + pmc->pring_va = dma_alloc_coherent(dev, sizeof(struct vring_tx_desc) * num_descriptors, &pmc->pring_pa, GFP_KERNEL); + if (wil->use_extended_dma_addr) + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); + wil_dbg_misc(wil, "pmc_alloc: allocated pring %p => %pad. %zd x %d = total %zd bytes\n", pmc->pring_va, &pmc->pring_pa, diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 072182e527e6..67f50ae17cd3 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -123,15 +123,32 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) vring->va = NULL; return -ENOMEM; } + /* vring->va should be aligned on its size rounded up to power of 2 - * This is granted by the dma_alloc_coherent + * This is granted by the dma_alloc_coherent. + * + * HW has limitation that all vrings addresses must share the same + * upper 16 msb bits part of 48 bits address. To workaround that, + * if we are using 48 bit addresses switch to 32 bit allocation + * before allocating vring memory. + * + * There's no check for the return value of dma_set_mask_and_coherent, + * since we assume if we were able to set the mask during + * initialization in this system it will not fail if we set it again */ + if (wil->use_extended_dma_addr) + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL); if (!vring->va) { kfree(vring->ctx); vring->ctx = NULL; return -ENOMEM; } + + if (wil->use_extended_dma_addr) + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); + /* initially, all descriptors are SW owned * For Tx and Rx, ownership bit is at the same location, thus * we can use any diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 085a2dbfa21d..ea1e5b34e011 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -657,6 +657,7 @@ struct wil6210_priv { u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */ struct wil_sta_info sta[WIL6210_MAX_CID]; int bcast_vring; + bool use_extended_dma_addr; /* indicates whether we are using 48 bits */ /* scan */ struct cfg80211_scan_request *scan_request; From 4a0e45a78328957b012f554fb2ffcd5c69fe418a Mon Sep 17 00:00:00 2001 From: Lior David Date: Wed, 8 Mar 2017 13:52:11 +0200 Subject: [PATCH 07/24] wil6210: do not start regular scan on stopped p2p device The driver should not allow starting any type of scan on a stopped P2P device. Current implementation only checked social scan. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 83155b5ddbfb..14aa6d36fd1e 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -390,22 +390,23 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, } mutex_unlock(&wil->p2p_wdev_mutex); - /* social scan on P2P_DEVICE is handled as p2p search */ - if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE && - wil_p2p_is_social_scan(request)) { + if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) { if (!wil->p2p.p2p_dev_started) { wil_err(wil, "P2P search requested on stopped P2P device\n"); rc = -EIO; goto out; } - wil->scan_request = request; - wil->radio_wdev = wdev; - rc = wil_p2p_search(wil, request); - if (rc) { - wil->radio_wdev = wil_to_wdev(wil); - wil->scan_request = NULL; + /* social scan on P2P_DEVICE is handled as p2p search */ + if (wil_p2p_is_social_scan(request)) { + wil->scan_request = request; + wil->radio_wdev = wdev; + rc = wil_p2p_search(wil, request); + if (rc) { + wil->radio_wdev = wil_to_wdev(wil); + wil->scan_request = NULL; + } + goto out; } - goto out; } (void)wil_p2p_stop_discovery(wil); From 9953a782f9fac1becccb8f48f1a276c310f2ab5a Mon Sep 17 00:00:00 2001 From: Lior David Date: Wed, 8 Mar 2017 13:52:12 +0200 Subject: [PATCH 08/24] wil6210: bus_request platform operation refinement The driver uses the bus_request platform operation to request resources from the platform for a specific bandwidth. Currently the driver requests resources for the maximum theoretical bandwidth, when interface is brought up. Refine this process a bit: now the driver will request a small amount of resources when interface is up, and will only issue the maximum request when connected. This mechanism will be improved further in the future to make more refined requests based on actual bandwidth. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 4 ++++ drivers/net/wireless/ath/wil6210/main.c | 14 +++++++++----- drivers/net/wireless/ath/wil6210/wil6210.h | 5 +++-- drivers/net/wireless/ath/wil6210/wmi.c | 1 + 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 14aa6d36fd1e..2dc594d98862 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -680,6 +680,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); if (rc == 0) { netif_carrier_on(ndev); + wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS); /* Connect can take lots of time */ mod_timer(&wil->connect_timer, jiffies + msecs_to_jiffies(2000)); @@ -1199,6 +1200,7 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy, wil->pbss = pbss; netif_carrier_on(ndev); + wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS); rc = wmi_pcp_start(wil, bi, wmi_nettype, chan, hidden_ssid, is_go); if (rc) @@ -1214,6 +1216,7 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy, wmi_pcp_stop(wil); err_pcp_start: netif_carrier_off(ndev); + wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); out: mutex_unlock(&wil->mutex); return rc; @@ -1320,6 +1323,7 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy, wil_dbg_misc(wil, "stop_ap\n"); netif_carrier_off(ndev); + wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); wil_set_recovery_state(wil, fw_recovery_idle); mutex_lock(&wil->mutex); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index efb1f59aafd9..21b7faca2c9b 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -274,6 +274,7 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, wil_bcast_fini(wil); wil_update_net_queues_bh(wil, NULL, true); netif_carrier_off(ndev); + wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); if (test_bit(wil_status_fwconnected, wil->status)) { clear_bit(wil_status_fwconnected, wil->status); @@ -555,6 +556,12 @@ int wil_priv_init(struct wil6210_priv *wil) return -EAGAIN; } +void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps) +{ + if (wil->platform_ops.bus_request) + wil->platform_ops.bus_request(wil->platform_handle, kbps); +} + /** * wil6210_disconnect - disconnect one connection * @wil: driver context @@ -1066,9 +1073,7 @@ int __wil_up(struct wil6210_priv *wil) napi_enable(&wil->napi_tx); set_bit(wil_status_napi_en, wil->status); - if (wil->platform_ops.bus_request) - wil->platform_ops.bus_request(wil->platform_handle, - WIL_MAX_BUS_REQUEST_KBPS); + wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); return 0; } @@ -1092,8 +1097,7 @@ int __wil_down(struct wil6210_priv *wil) set_bit(wil_status_resetting, wil->status); - if (wil->platform_ops.bus_request) - wil->platform_ops.bus_request(wil->platform_handle, 0); + wil6210_bus_request(wil, 0); wil_disable_irq(wil); if (test_and_clear_bit(wil_status_napi_en, wil->status)) { diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index ea1e5b34e011..12de74a38f8d 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -40,6 +40,7 @@ extern bool disable_ap_sme; #define WIL_FW_NAME_SPARROW_PLUS "wil6210_sparrow_plus.fw" /* code Sparrow D0 */ #define WIL_BOARD_FILE_NAME "wil6210.brd" /* board & radio parameters */ +#define WIL_DEFAULT_BUS_REQUEST_KBPS 128000 /* ~1Gbps */ #define WIL_MAX_BUS_REQUEST_KBPS 800000 /* ~6.1Gbps */ /** @@ -900,7 +901,7 @@ int wmi_pcp_stop(struct wil6210_priv *wil); int wmi_led_cfg(struct wil6210_priv *wil, bool enable); int wmi_abort_scan(struct wil6210_priv *wil); void wil_abort_scan(struct wil6210_priv *wil, bool sync); - +void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps); void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, u16 reason_code, bool from_event); void wil_probe_client_flush(struct wil6210_priv *wil); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 1f22c19696b1..a7a4ac1a8eb7 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -565,6 +565,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { if (rc) { netif_carrier_off(ndev); + wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); wil_err(wil, "cfg80211_connect_result with failure\n"); cfg80211_connect_result(ndev, evt->bssid, NULL, 0, NULL, 0, From 5eb443e9af87776f94e77a9b72e54ceb81cbabc1 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 8 Mar 2017 13:52:13 +0200 Subject: [PATCH 09/24] wil6210: use print_hex_dump_debug instead of print_hex_dump_bytes Some dynamic debug printouts in driver are using print_hex_dump_bytes. However, with dynamic debug disabled, print_hex_dump_bytes outputs to log unconditionally. Use print_hex_dump_debug instead to prevent log pollution when dynamic debug disabled. Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 41 +++++++++++---------- drivers/net/wireless/ath/wil6210/wil6210.h | 12 ++++++ 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 2dc594d98862..581a4e2966fc 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -416,9 +416,9 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, for (i = 0; i < request->n_ssids; i++) { wil_dbg_misc(wil, "SSID[%d]", i); - print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET, - request->ssids[i].ssid, - request->ssids[i].ssid_len); + wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1, + request->ssids[i].ssid, + request->ssids[i].ssid_len, true); } if (request->n_ssids) @@ -455,8 +455,8 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, } if (request->ie_len) - print_hex_dump_bytes("Scan IE ", DUMP_PREFIX_OFFSET, - request->ie, request->ie_len); + wil_hex_dump_misc("Scan IE ", DUMP_PREFIX_OFFSET, 16, 1, + request->ie, request->ie_len, true); else wil_dbg_misc(wil, "Scan has no IE's\n"); @@ -762,7 +762,8 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, */ wil_dbg_misc(wil, "mgmt_tx\n"); - print_hex_dump_bytes("mgmt tx frame ", DUMP_PREFIX_OFFSET, buf, len); + wil_hex_dump_misc("mgmt tx frame ", DUMP_PREFIX_OFFSET, 16, 1, buf, + len, true); cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL); if (!cmd) { @@ -1095,18 +1096,18 @@ static int _wil_cfg80211_merge_extra_ies(const u8 *ies1, u16 ies1_len, static void wil_print_bcon_data(struct cfg80211_beacon_data *b) { - print_hex_dump_bytes("head ", DUMP_PREFIX_OFFSET, - b->head, b->head_len); - print_hex_dump_bytes("tail ", DUMP_PREFIX_OFFSET, - b->tail, b->tail_len); - print_hex_dump_bytes("BCON IE ", DUMP_PREFIX_OFFSET, - b->beacon_ies, b->beacon_ies_len); - print_hex_dump_bytes("PROBE ", DUMP_PREFIX_OFFSET, - b->probe_resp, b->probe_resp_len); - print_hex_dump_bytes("PROBE IE ", DUMP_PREFIX_OFFSET, - b->proberesp_ies, b->proberesp_ies_len); - print_hex_dump_bytes("ASSOC IE ", DUMP_PREFIX_OFFSET, - b->assocresp_ies, b->assocresp_ies_len); + wil_hex_dump_misc("head ", DUMP_PREFIX_OFFSET, 16, 1, + b->head, b->head_len, true); + wil_hex_dump_misc("tail ", DUMP_PREFIX_OFFSET, 16, 1, + b->tail, b->tail_len, true); + wil_hex_dump_misc("BCON IE ", DUMP_PREFIX_OFFSET, 16, 1, + b->beacon_ies, b->beacon_ies_len, true); + wil_hex_dump_misc("PROBE ", DUMP_PREFIX_OFFSET, 16, 1, + b->probe_resp, b->probe_resp_len, true); + wil_hex_dump_misc("PROBE IE ", DUMP_PREFIX_OFFSET, 16, 1, + b->proberesp_ies, b->proberesp_ies_len, true); + wil_hex_dump_misc("ASSOC IE ", DUMP_PREFIX_OFFSET, 16, 1, + b->assocresp_ies, b->assocresp_ies_len, true); } /* internal functions for device reset and starting AP */ @@ -1302,8 +1303,8 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, wil_dbg_misc(wil, "BI %d DTIM %d\n", info->beacon_interval, info->dtim_period); wil_dbg_misc(wil, "PBSS %d\n", info->pbss); - print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET, - info->ssid, info->ssid_len); + wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1, + info->ssid, info->ssid_len, true); wil_print_bcon_data(bcon); wil_print_crypto(wil, crypto); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 12de74a38f8d..89e7eb7790fa 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -766,6 +766,12 @@ static inline void wil_c(struct wil6210_priv *wil, u32 reg, u32 val) print_hex_dump_debug("DBG[ WMI]" prefix_str,\ prefix_type, rowsize, \ groupsize, buf, len, ascii) + +#define wil_hex_dump_misc(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + print_hex_dump_debug("DBG[MISC]" prefix_str,\ + prefix_type, rowsize, \ + groupsize, buf, len, ascii) #else /* defined(CONFIG_DYNAMIC_DEBUG) */ static inline void wil_hex_dump_txrx(const char *prefix_str, int prefix_type, int rowsize, @@ -778,6 +784,12 @@ void wil_hex_dump_wmi(const char *prefix_str, int prefix_type, int rowsize, int groupsize, const void *buf, size_t len, bool ascii) { } + +static inline +void wil_hex_dump_misc(const char *prefix_str, int prefix_type, int rowsize, + int groupsize, const void *buf, size_t len, bool ascii) +{ +} #endif /* defined(CONFIG_DYNAMIC_DEBUG) */ void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, From 18618a9fba33f87a1cc6083a22a541972cd3f3b3 Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Wed, 8 Mar 2017 13:52:14 +0200 Subject: [PATCH 10/24] wil6210: missing reinit_completion in HALP voting After setting HALP ICR bit, we keep it set until HALP unvote. Masking HALP ICR should protect the driver from hitting the HALP ICR over and over again. However, in case there is another MISC ICR we will read the HALP ICR and issue a completion. This can lead to a case where HALP voting is completed immediately, as the completion is already set. Reinit the HALP completion before the actual vote will clear previous completions and protect from such cases. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 21b7faca2c9b..dddde94f0356 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -1158,6 +1158,7 @@ void wil_halp_vote(struct wil6210_priv *wil) wil->halp.ref_cnt); if (++wil->halp.ref_cnt == 1) { + reinit_completion(&wil->halp.comp); wil6210_set_halp(wil); rc = wait_for_completion_timeout(&wil->halp.comp, to_jiffies); if (!rc) { From bcdd49b074d0ea48f54bc6d24a66efab8688b43a Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 8 Mar 2017 13:52:15 +0200 Subject: [PATCH 11/24] wil6210: store bss object and use cfg80211_connect_bss() In a fast disconnect/connect sequence, cfg80211_connect_result() can fail to find the bss object which the driver is connecting to. Detailed sequence of events: * Driver is connected in STA mode * Disconnect request arrives from user space. Driver disconnects and calls cfg80211_disconnected() which adds new event to the cfg80211_wq worker thread * Connect request arrives from user space. cfg80211_connect() stores ssid/ssid_len and calls rdev_connect() * __cfg80211_disconnected() runs in worker thread and zero wdev->ssid_len * Connect succeeds. Driver calls cfg80211_connect_result() which fails to find the bss because wdev->ssid_len is zero To overcome this, upon connect request, store the bss object in the driver and upon connect completion pass it to kernel using cfg80211_connect_bss(). Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 1 + drivers/net/wireless/ath/wil6210/main.c | 1 + drivers/net/wireless/ath/wil6210/wil6210.h | 1 + drivers/net/wireless/ath/wil6210/wmi.c | 14 +++++++++----- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 581a4e2966fc..39ee565ecf56 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -681,6 +681,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, if (rc == 0) { netif_carrier_on(ndev); wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS); + wil->bss = bss; /* Connect can take lots of time */ mod_timer(&wil->connect_timer, jiffies + msecs_to_jiffies(2000)); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index dddde94f0356..5d45faca5d51 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -284,6 +284,7 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); + wil->bss = NULL; } clear_bit(wil_status_fwconnecting, wil->status); break; diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 89e7eb7790fa..09e2fcef4f07 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -613,6 +613,7 @@ struct wil6210_priv { u16 channel; /* relevant in AP mode */ int sinfo_gen; u32 ap_isolate; /* no intra-BSS communication */ + struct cfg80211_bss *bss; /* connected bss, relevant in STA mode */ /* interrupt moderation */ u32 tx_max_burst_duration; u32 tx_interframe_timeout; diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index a7a4ac1a8eb7..a2a895bee6d8 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -573,12 +573,16 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) GFP_KERNEL); goto out; } else { - cfg80211_connect_result(ndev, evt->bssid, - assoc_req_ie, assoc_req_ielen, - assoc_resp_ie, assoc_resp_ielen, - WLAN_STATUS_SUCCESS, - GFP_KERNEL); + struct wiphy *wiphy = wil_to_wiphy(wil); + + cfg80211_ref_bss(wiphy, wil->bss); + cfg80211_connect_bss(ndev, evt->bssid, wil->bss, + assoc_req_ie, assoc_req_ielen, + assoc_resp_ie, assoc_resp_ielen, + WLAN_STATUS_SUCCESS, GFP_KERNEL, + NL80211_TIMEOUT_UNSPECIFIED); } + wil->bss = NULL; } else if ((wdev->iftype == NL80211_IFTYPE_AP) || (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { if (rc) { From f6b29b6585593b28c4fde142727bfef350372b83 Mon Sep 17 00:00:00 2001 From: Hamad Kadmany Date: Wed, 8 Mar 2017 13:52:16 +0200 Subject: [PATCH 12/24] wil6210: protect list of pending wmi events during flush When flush is done, pending events list is manipulated without taking the proper spinlock, which could lead to memory corruption if list is manipulated by wmi worker or by interrupt routine. Signed-off-by: Hamad Kadmany Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/wmi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index a2a895bee6d8..02ccdaee6da9 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1738,14 +1738,19 @@ int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid) void wmi_event_flush(struct wil6210_priv *wil) { + ulong flags; struct pending_wmi_event *evt, *t; wil_dbg_wmi(wil, "event_flush\n"); + spin_lock_irqsave(&wil->wmi_ev_lock, flags); + list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) { list_del(&evt->list); kfree(evt); } + + spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); } static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id, From 628639b1c976a5f02792dc6595a4d199b7b70d85 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 8 Mar 2017 13:52:17 +0200 Subject: [PATCH 13/24] wil6210: use WMI_DISCONNECT_CMDID upon connect timeout Upon connect timeout driver invokes _wil6210_disconnect() which iterates over sta array and disconnects each connected sta. In practice, because the connection is still ongoing and because cid is not yet allocated, disconnect is not actually happening. This leaves FW in connecting state while driver is in disconnected state. To fix this, upon connect timeout, explicitly send WMI_DISCONNECT_CMDID to FW to make sure it gets disconnected. Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 30 ++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 5d45faca5d51..79aeb2b80055 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -306,10 +306,34 @@ static void wil_disconnect_worker(struct work_struct *work) { struct wil6210_priv *wil = container_of(work, struct wil6210_priv, disconnect_worker); + struct net_device *ndev = wil_to_ndev(wil); + int rc; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_disconnect_event evt; + } __packed reply; - mutex_lock(&wil->mutex); - _wil6210_disconnect(wil, NULL, WLAN_REASON_UNSPECIFIED, false); - mutex_unlock(&wil->mutex); + if (test_bit(wil_status_fwconnected, wil->status)) + /* connect succeeded after all */ + return; + + if (!test_bit(wil_status_fwconnecting, wil->status)) + /* already disconnected */ + return; + + rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0, + WMI_DISCONNECT_EVENTID, &reply, sizeof(reply), + WIL6210_DISCONNECT_TO_MS); + if (rc) { + wil_err(wil, "disconnect error %d\n", rc); + return; + } + + wil_update_net_queues_bh(wil, NULL, true); + netif_carrier_off(ndev); + cfg80211_connect_result(ndev, NULL, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); + clear_bit(wil_status_fwconnecting, wil->status); } static void wil_connect_timer_fn(ulong x) From 3b56c15fa3315942b4cb02e57ff842f2f9b14ef7 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 8 Mar 2017 13:52:18 +0200 Subject: [PATCH 14/24] wil6210: correctly report locally generated disconnect in STA mode Driver always invoke cfg80211_disconnected() with locally_generated as false. Fix this by reporting true whenever the disconnect is triggered from upper layers (cfg80211) or from within the driver itself (reset, deinit). Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 1 + drivers/net/wireless/ath/wil6210/main.c | 5 ++++- drivers/net/wireless/ath/wil6210/wil6210.h | 1 + drivers/net/wireless/ath/wil6210/wmi.c | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 39ee565ecf56..1981ec2e0186 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -710,6 +710,7 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy, return 0; } + wil->locally_generated_disc = true; rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0, WMI_DISCONNECT_EVENTID, NULL, 0, WIL6210_DISCONNECT_TO_MS); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 79aeb2b80055..3206aea00ccd 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -279,7 +279,10 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, if (test_bit(wil_status_fwconnected, wil->status)) { clear_bit(wil_status_fwconnected, wil->status); cfg80211_disconnected(ndev, reason_code, - NULL, 0, false, GFP_KERNEL); + NULL, 0, + wil->locally_generated_disc, + GFP_KERNEL); + wil->locally_generated_disc = false; } else if (test_bit(wil_status_fwconnecting, wil->status)) { cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 09e2fcef4f07..cb825ccedbe0 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -614,6 +614,7 @@ struct wil6210_priv { int sinfo_gen; u32 ap_isolate; /* no intra-BSS communication */ struct cfg80211_bss *bss; /* connected bss, relevant in STA mode */ + int locally_generated_disc; /* relevant in STA mode */ /* interrupt moderation */ u32 tx_max_burst_duration; u32 tx_interframe_timeout; diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 02ccdaee6da9..9255c47af15a 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1497,6 +1497,7 @@ int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, wil_dbg_wmi(wil, "disconnect_sta: (%pM, reason %d)\n", mac, reason); + wil->locally_generated_disc = true; if (del_sta) { ether_addr_copy(del_sta_cmd.dst_mac, mac); rc = wmi_call(wil, WMI_DEL_STA_CMDID, &del_sta_cmd, From b8c31b5d6c232a36c821bdd556b60c033a1c1577 Mon Sep 17 00:00:00 2001 From: Lior David Date: Wed, 8 Mar 2017 13:52:19 +0200 Subject: [PATCH 15/24] wil6210: add oob_mode for AP certification Add a new value to the oob_mode module parameter for supporting AP certification. All enabled values of oob_mode (>0) are intended only for debugging and diagnostics. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 24 ++++++++++++++++------ drivers/net/wireless/ath/wil6210/wil6210.h | 1 + 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 3206aea00ccd..c33cc4ad44c4 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -30,8 +30,8 @@ bool debug_fw; /* = false; */ module_param(debug_fw, bool, 0444); MODULE_PARM_DESC(debug_fw, " do not perform card reset. For FW debug"); -static bool oob_mode; -module_param(oob_mode, bool, 0444); +static u8 oob_mode; +module_param(oob_mode, byte, 0444); MODULE_PARM_DESC(oob_mode, " enable out of the box (OOB) mode in FW, for diagnostics and certification"); @@ -642,13 +642,25 @@ static inline void wil_release_cpu(struct wil6210_priv *wil) wil_w(wil, RGF_USER_USER_CPU_0, 1); } -static void wil_set_oob_mode(struct wil6210_priv *wil, bool enable) +static void wil_set_oob_mode(struct wil6210_priv *wil, u8 mode) { - wil_info(wil, "enable=%d\n", enable); - if (enable) + wil_info(wil, "oob_mode to %d\n", mode); + switch (mode) { + case 0: + wil_c(wil, RGF_USER_USAGE_6, BIT_USER_OOB_MODE | + BIT_USER_OOB_R2_MODE); + break; + case 1: + wil_c(wil, RGF_USER_USAGE_6, BIT_USER_OOB_R2_MODE); wil_s(wil, RGF_USER_USAGE_6, BIT_USER_OOB_MODE); - else + break; + case 2: wil_c(wil, RGF_USER_USAGE_6, BIT_USER_OOB_MODE); + wil_s(wil, RGF_USER_USAGE_6, BIT_USER_OOB_R2_MODE); + break; + default: + wil_err(wil, "invalid oob_mode: %d\n", mode); + } } static int wil_target_reset(struct wil6210_priv *wil) diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index cb825ccedbe0..fee18916b713 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -140,6 +140,7 @@ struct RGF_ICR { #define RGF_USER_USAGE_1 (0x880004) #define RGF_USER_USAGE_6 (0x880018) #define BIT_USER_OOB_MODE BIT(31) + #define BIT_USER_OOB_R2_MODE BIT(30) #define RGF_USER_HW_MACHINE_STATE (0x8801dc) #define HW_MACHINE_BOOT_DONE (0x3fffffd) #define RGF_USER_USER_CPU_0 (0x8801e0) From ca07baab0b1e627ae1d4a55d190fb1c9d32a3445 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Wed, 22 Feb 2017 21:03:11 +0530 Subject: [PATCH 16/24] ath10k: disallow DFS simulation if DFS channel is not enabled If DFS is not enabled in hostapd (ieee80211h=0) DFS channels shall not be available for use even though the hardware may have the capability to support DFS. With this configuration (DFS disabled in hostapd) trying to bring up ath10k device in DFS channel for AP mode fails and trying to simulate DFS in ath10k debugfs results in a warning in cfg80211 complaining invalid channel and this should be avoided in the driver itself rather than false propogating RADAR detection to mac80211/cfg80211. Fix this by checking for the first vif 'is_started' state(should work for client mode as well) as all the vifs shall be configured for the same channel sys/kernel/debug/ieee80211/phy1/ath10k# echo 1 > dfs_simulate_radar WARNING: at net/wireless/chan.c:265 cfg80211_radar_event+0x24/0x60 Workqueue: phy0 ieee80211_dfs_radar_detected_work [mac80211] [] (warn_slowpath_null) from [] (cfg80211_radar_event+0x24/0x60 [cfg80211]) [] (cfg80211_radar_event [cfg80211]) from [] (ieee80211_dfs_radar_detected_work+0x94/0xa0 [mac80211]) [] (ieee80211_dfs_radar_detected_work [mac80211]) from [] (process_one_work+0x20c/0x32c) WARNING: at net/wireless/nl80211.c:2488 nl80211_get_mpath+0x13c/0x4cc Workqueue: phy0 ieee80211_dfs_radar_detected_work [mac80211] [] (warn_slowpath_null) from [] (cfg80211_radar_event+0x24/0x60 [cfg80211]) [] (cfg80211_radar_event [cfg80211]) from [] (ieee80211_dfs_radar_detected_work+0x94/0xa0 [mac80211]) [] (ieee80211_dfs_radar_detected_work [mac80211]) from [] (process_one_work+0x20c/0x32c) Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index fb0ade3adb07..ac9090b752fc 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -1997,6 +1997,15 @@ static ssize_t ath10k_write_simulate_radar(struct file *file, size_t count, loff_t *ppos) { struct ath10k *ar = file->private_data; + struct ath10k_vif *arvif; + + /* Just check for for the first vif alone, as all the vifs will be + * sharing the same channel and if the channel is disabled, all the + * vifs will share the same 'is_started' state. + */ + arvif = list_first_entry(&ar->arvifs, typeof(*arvif), list); + if (!arvif->is_started) + return -EINVAL; ieee80211_radar_detected(ar->hw); From 523f6701dbabbe63b35d16bb676d35212b22e204 Mon Sep 17 00:00:00 2001 From: Tamizh chelvam Date: Thu, 23 Feb 2017 18:48:22 +0530 Subject: [PATCH 17/24] ath10k: update available channel list for 5G radio If a 5 GHz radio is calibrated for operation in both the low band (channels 36 to 64) and high band(channels 100 to 169), hardware allows operations in all the listed channels. However, if the chip has been calibrated only for the low/high band and a high/low band channel is configured, due to lack of calibration there will be potentially invalid signal on those non calibrated channels. To avoid this problem this patch sets IEEE80211_CHAN_DISABLED flag for those non calibrated channels by using low_5ghz_chan and high_5ghz_chan values which we get from target through wmi service ready event. Driver initialized flags are getting re initialized in handle_channel in cfg80211. So calling the function to disable the non supported channel from reg_notifier(). Signed-off-by: Tamizh chelvam Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 2 ++ drivers/net/wireless/ath/ath10k/mac.c | 19 +++++++++++++++++++ drivers/net/wireless/ath/ath10k/wmi.c | 6 ++++++ drivers/net/wireless/ath/ath10k/wmi.h | 2 ++ 4 files changed, 29 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 88d14be7fcce..d4b9a0ec1bdc 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -775,6 +775,8 @@ struct ath10k { u32 num_rf_chains; u32 max_spatial_stream; /* protected by conf_mutex */ + u32 low_5ghz_chan; + u32 high_5ghz_chan; bool ani_enabled; bool p2p; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index f0e46804ad82..167bde5cbbad 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3126,6 +3126,21 @@ static void ath10k_regd_update(struct ath10k *ar) ath10k_warn(ar, "failed to set pdev regdomain: %d\n", ret); } +void ath10k_mac_update_channel_list(struct ath10k *ar, + struct ieee80211_supported_band *band) +{ + int i; + + if (ar->low_5ghz_chan && ar->high_5ghz_chan) { + for (i = 0; i < band->n_channels; i++) { + if (band->channels[i].center_freq < ar->low_5ghz_chan || + band->channels[i].center_freq > ar->high_5ghz_chan) + band->channels[i].flags |= + IEEE80211_CHAN_DISABLED; + } + } +} + static void ath10k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { @@ -3149,6 +3164,10 @@ static void ath10k_reg_notifier(struct wiphy *wiphy, if (ar->state == ATH10K_STATE_ON) ath10k_regd_update(ar); mutex_unlock(&ar->conf_mutex); + + if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY) + ath10k_mac_update_channel_list(ar, + ar->hw->wiphy->bands[NL80211_BAND_5GHZ]); } /***************/ diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 2f1743e60fa1..b31c4f71b413 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -4593,6 +4593,8 @@ ath10k_wmi_main_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb, arg->phy_capab = ev->phy_capability; arg->num_rf_chains = ev->num_rf_chains; arg->eeprom_rd = ev->hal_reg_capabilities.eeprom_rd; + arg->low_5ghz_chan = ev->hal_reg_capabilities.low_5ghz_chan; + arg->high_5ghz_chan = ev->hal_reg_capabilities.high_5ghz_chan; arg->num_mem_reqs = ev->num_mem_reqs; arg->service_map = ev->wmi_service_bitmap; arg->service_map_len = sizeof(ev->wmi_service_bitmap); @@ -4629,6 +4631,8 @@ ath10k_wmi_10x_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb, arg->phy_capab = ev->phy_capability; arg->num_rf_chains = ev->num_rf_chains; arg->eeprom_rd = ev->hal_reg_capabilities.eeprom_rd; + arg->low_5ghz_chan = ev->hal_reg_capabilities.low_5ghz_chan; + arg->high_5ghz_chan = ev->hal_reg_capabilities.high_5ghz_chan; arg->num_mem_reqs = ev->num_mem_reqs; arg->service_map = ev->wmi_service_bitmap; arg->service_map_len = sizeof(ev->wmi_service_bitmap); @@ -4682,6 +4686,8 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work) ar->phy_capability = __le32_to_cpu(arg.phy_capab); ar->num_rf_chains = __le32_to_cpu(arg.num_rf_chains); ar->hw_eeprom_rd = __le32_to_cpu(arg.eeprom_rd); + ar->low_5ghz_chan = __le32_to_cpu(arg.low_5ghz_chan); + ar->high_5ghz_chan = __le32_to_cpu(arg.high_5ghz_chan); ath10k_dbg_dump(ar, ATH10K_DBG_WMI, NULL, "wmi svc: ", arg.service_map, arg.service_map_len); diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 386aa51435f1..78350d4c7b79 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -6335,6 +6335,8 @@ struct wmi_svc_rdy_ev_arg { __le32 num_rf_chains; __le32 eeprom_rd; __le32 num_mem_reqs; + __le32 low_5ghz_chan; + __le32 high_5ghz_chan; const __le32 *service_map; size_t service_map_len; const struct wlan_host_mem_req *mem_reqs[WMI_MAX_MEM_REQS]; From 45c3d550b0684343b9d4b25aeec9b49c6effc1f9 Mon Sep 17 00:00:00 2001 From: Zefir Kurtisi Date: Mon, 27 Feb 2017 15:49:36 +0100 Subject: [PATCH 18/24] ath9k: don't trigger spectral scan when not enabled Doing so enables the FFT generation without prior configuration, leading to an IRQ storm caused by invalid (or at least unwanted) PHY errors. Signed-off-by: Zefir Kurtisi Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/common-spectral.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c index 0ffa23a61568..5e77fe1f5b0d 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.c +++ b/drivers/net/wireless/ath/ath9k/common-spectral.c @@ -742,6 +742,9 @@ void ath9k_cmn_spectral_scan_trigger(struct ath_common *common, return; } + if (!spec_priv->spec_config.enabled) + return; + ath_ps_ops(common)->wakeup(common); rxfilter = ath9k_hw_getrxfilter(ah); ath9k_hw_setrxfilter(ah, rxfilter | From 51f60b8b6979da361c97602d6b6df3c3f72c7c4c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 28 Feb 2017 11:14:39 +0000 Subject: [PATCH 19/24] ath10k: remove redundant error check The check on ret for an error is redundant because it is already been checked for non-zero earlier on and ret is never non-zero at this point. Fix this by removing the redundant check and error message. Detected by CoverityScan, CID#1357170 ("Logically Dead Code") Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 6094372307aa..52896c20ca4e 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -970,12 +970,6 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, } remaining_bytes -= nbytes; - - if (ret) { - ath10k_warn(ar, "failed to read diag value at 0x%x: %d\n", - address, ret); - break; - } memcpy(data, data_buf, nbytes); address += nbytes; From a28f6f27a88f047f03f04b9246ca260ebc91455e Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Wed, 8 Mar 2017 18:03:32 +0530 Subject: [PATCH 20/24] ath10k: fix fetching channel during potential radar detection Fetch target operating channel during potential radar detection when the interface is just brought up, but no channel is assigned from userspace. In this scenario rx_channel may not be having a valid pointer hence fetch the target operating channel to avoid warnings as below which can be triggered by the commands with DFS testing over longer run comamnds: iw wlan1 set type mesh ifconfig wlan1 up (valid tgt_oper_chan only) iw wlan1 cac trigger freq 5260 HT20 (valid rx_channel, tgt_oper_chan) iw wlan1 cac trigger freq 5280 HT20 iw wlan1 cac trigger freq 5300 HT20 Once the CAC expires, current channel context will be removed and we are only left with the fallback option of using 'target operating channel' Firmware and driver log: ath: phy1: DFS: radar found on freq=5300: id=1, pri=1125, count=5, count_false=4 ath: phy1: DFS: radar found on freq=5260: id=5, pri=3151, count=6, count_false=11 ath: phy1: DFS: radar found on freq=5280: id=1, pri=1351, count=6, count_false=4 ath: phy1: DFS: radar found on freq=5300: id=1, pri=1125, count=5, count_false=4 ath10k_pci 0001:01:00.0: failed to derive channel for radar pulse, treating as radar ath10k_pci 0001:01:00.0: failed to derive channel for radar pulse, treating as radar Call trace: WARNING: CPU: 1 PID: 2145 at backports-20161201-3.14.77-9ab3068/net/wireless/chan.c:265 cfg80211_set_dfs_state+0x3c/0x88 [cfg80211]() Workqueue: phy1 ieee80211_dfs_radar_detected_work [mac80211] [] (warn_slowpath_null) from [] (cfg80211_set_dfs_state+0x3c/0x88 [cfg80211]) [] (cfg80211_set_dfs_state [cfg80211]) from [] (cfg80211_radar_event+0xc4/0x140 [cfg80211]) [] (cfg80211_radar_event [cfg80211]) from [] (ieee80211_dfs_radar_detected_work+0xa8/0xb4 [mac80211]) [] (ieee80211_dfs_radar_detected_work [mac80211]) from [] (process_one_work+0x298/0x4a4) Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index b31c4f71b413..4e60caec7ab4 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -3643,6 +3643,11 @@ static void ath10k_dfs_radar_report(struct ath10k *ar, spin_lock_bh(&ar->data_lock); ch = ar->rx_channel; + + /* fetch target operating channel during channel change */ + if (!ch) + ch = ar->tgt_oper_chan; + spin_unlock_bh(&ar->data_lock); if (!ch) { From 46bc92bedbd3cc9594bf572897e6a38346067176 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 16 Mar 2017 11:11:02 +0200 Subject: [PATCH 21/24] ath10k: fix warnings from an earlier commit I failed to notice that commit 523f6701dbab ("ath10k: update available channel list for 5G radio") added two new warnings: drivers/net/wireless/ath/ath10k/mac.c:3129:6: warning: symbol 'ath10k_mac_update_channel_list' was not declared. Should it be static? drivers/net/wireless/ath/ath10k/mac.c:3170: Alignment should match open parenthesis Fix those. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 167bde5cbbad..968b1d421225 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3126,8 +3126,8 @@ static void ath10k_regd_update(struct ath10k *ar) ath10k_warn(ar, "failed to set pdev regdomain: %d\n", ret); } -void ath10k_mac_update_channel_list(struct ath10k *ar, - struct ieee80211_supported_band *band) +static void ath10k_mac_update_channel_list(struct ath10k *ar, + struct ieee80211_supported_band *band) { int i; @@ -3167,7 +3167,7 @@ static void ath10k_reg_notifier(struct wiphy *wiphy, if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY) ath10k_mac_update_channel_list(ar, - ar->hw->wiphy->bands[NL80211_BAND_5GHZ]); + ar->hw->wiphy->bands[NL80211_BAND_5GHZ]); } /***************/ From 0a7d88e432e7fc968dee1ddbd214cf1bb966b6f9 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Tue, 14 Mar 2017 17:03:24 +0530 Subject: [PATCH 22/24] ath10k: fix typo in wmi header file Fix typo (spelling mistake) in wmi.h Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 78350d4c7b79..cf385feb5707 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -3182,7 +3182,7 @@ struct wmi_10_4_phyerr_event { struct phyerr_radar_report { __le32 reg0; /* RADAR_REPORT_REG0_* */ - __le32 reg1; /* REDAR_REPORT_REG1_* */ + __le32 reg1; /* RADAR_REPORT_REG1_* */ } __packed; #define RADAR_REPORT_REG0_PULSE_IS_CHIRP_MASK 0x80000000 From eed4721fb6172b3051a8341f9089b728681a9454 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 15 Mar 2017 16:36:41 +0000 Subject: [PATCH 23/24] ath10k: remove redundant check of len with buf_len The check of len > buf_len is redundant as len is initialized to 0 and buf_len to 4096, so this comparison is always false. Remove it. Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index ac9090b752fc..00b424d99126 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -249,9 +249,6 @@ static ssize_t ath10k_read_wmi_services(struct file *file, mutex_lock(&ar->conf_mutex); - if (len > buf_len) - len = buf_len; - spin_lock_bh(&ar->data_lock); for (i = 0; i < WMI_SERVICE_MAX; i++) { enabled = test_bit(i, ar->wmi.svc_map); From b51040fc375c194e20164be489668d4d28001f53 Mon Sep 17 00:00:00 2001 From: Rostyslav Khudolii Date: Thu, 16 Mar 2017 11:22:17 +0200 Subject: [PATCH 24/24] ath6kl: Remove old 802.11a-only channels Channels 34/38/42/46 can only be used for compatibility with old devices sold in Japan. Modern products, such as AR6003/AR6004 don't support these channels. Keeping them in the upstream is error prone and requires full network stack support. A custom patch should be used in case such compatibility is required. Without this one, a user is able to start an AP using wpa_supplicant, for example, on one of these channels (34/38/42/46), without getting any warning/error from the cfg80211 or ath6kl - which is correct (since these channels match regdom rules). However, the AR6003 and its firmware (we're using v3.4.0.225) will fail and return "WMI_CMDERROR_EVENTID" with "INVALID_PARAM" error code. Signed-off-by: Rostyslav Khudolii Cc: Attila Sukosd Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 363b30a549c2..aae65ce9a2b1 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -102,10 +102,8 @@ static struct ieee80211_channel ath6kl_2ghz_channels[] = { }; static struct ieee80211_channel ath6kl_5ghz_a_channels[] = { - CHAN5G(34, 0), CHAN5G(36, 0), - CHAN5G(38, 0), CHAN5G(40, 0), - CHAN5G(42, 0), CHAN5G(44, 0), - CHAN5G(46, 0), CHAN5G(48, 0), + CHAN5G(36, 0), CHAN5G(40, 0), + CHAN5G(44, 0), CHAN5G(48, 0), CHAN5G(52, 0), CHAN5G(56, 0), CHAN5G(60, 0), CHAN5G(64, 0), CHAN5G(100, 0), CHAN5G(104, 0),