diff options
Diffstat (limited to 'drivers/net/wireless/intel')
65 files changed, 2086 insertions, 998 deletions
diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index 236b52423506..7c4f550a1475 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -3732,7 +3732,7 @@ IPW2100_ORD(STAT_TX_HOST_REQUESTS, "requested Host Tx's (MSDU)"), IPW2100_ORD(ASSOCIATED_AP_PTR, "0 if not associated, else pointer to AP table entry"), IPW2100_ORD(AVAILABLE_AP_CNT, - "AP's decsribed in the AP table"), + "AP's described in the AP table"), IPW2100_ORD(AP_LIST_PTR, "Ptr to list of available APs"), IPW2100_ORD(STAT_AP_ASSNS, "associations"), IPW2100_ORD(STAT_ASSN_FAIL, "association failures"), diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.h b/drivers/net/wireless/intel/ipw2x00/ipw2100.h index 193947865efd..ce3e35f6b60f 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.h +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.h @@ -1009,7 +1009,7 @@ typedef enum _ORDINAL_TABLE_1 { // NS - means Not Supported by FW IPW_ORD_STAT_PERCENT_RETRIES, // current calculation of % missed tx retries IPW_ORD_ASSOCIATED_AP_PTR, // If associated, this is ptr to the associated // AP table entry. set to 0 if not associated - IPW_ORD_AVAILABLE_AP_CNT, // # of AP's decsribed in the AP table + IPW_ORD_AVAILABLE_AP_CNT, // # of AP's described in the AP table IPW_ORD_AP_LIST_PTR, // Ptr to list of available APs IPW_ORD_STAT_AP_ASSNS, // # of associations IPW_ORD_STAT_ASSN_FAIL, // # of association failures diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index 87a5e414c2f7..f26beeb6c5ff 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -7557,8 +7557,7 @@ static int ipw_associate(void *data) } if (priv->status & STATUS_DISASSOCIATING) { - IPW_DEBUG_ASSOC("Not attempting association (in " - "disassociating)\n "); + IPW_DEBUG_ASSOC("Not attempting association (in disassociating)\n"); schedule_work(&priv->associate); return 0; } @@ -12012,7 +12011,7 @@ MODULE_PARM_DESC(rtap_iface, "create the rtap interface (1 - create, default 0)" #ifdef CONFIG_IPW2200_QOS module_param(qos_enable, int, 0444); -MODULE_PARM_DESC(qos_enable, "enable all QoS functionalitis"); +MODULE_PARM_DESC(qos_enable, "enable all QoS functionalities"); module_param(qos_burst_enable, int, 0444); MODULE_PARM_DESC(qos_burst_enable, "enable QoS burst mode"); diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile index e6205eae51fd..4d08d78c6b71 100644 --- a/drivers/net/wireless/intel/iwlwifi/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/Makefile @@ -13,7 +13,7 @@ iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/22000.o iwlwifi-objs += iwl-trans.o iwlwifi-objs += fw/notif-wait.o iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o fw/init.o fw/dbg.o -iwlwifi-$(CONFIG_IWLMVM) += fw/common_rx.o fw/nvm.o +iwlwifi-$(CONFIG_IWLMVM) += fw/common_rx.o iwlwifi-$(CONFIG_ACPI) += fw/acpi.o iwlwifi-$(CONFIG_IWLWIFI_DEBUGFS) += fw/debugfs.o diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/1000.c b/drivers/net/wireless/intel/iwlwifi/cfg/1000.c index b2573b1d1506..591687984962 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/1000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/1000.c @@ -1,6 +1,7 @@ /****************************************************************************** * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -27,7 +28,6 @@ #include <linux/module.h> #include <linux/stringify.h> #include "iwl-config.h" -#include "iwl-csr.h" #include "iwl-agn-hw.h" /* Highest firmware API version supported */ @@ -91,7 +91,8 @@ static const struct iwl_eeprom_params iwl1000_eeprom_params = { .base_params = &iwl1000_base_params, \ .eeprom_params = &iwl1000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .csr = &iwl_csr_v1 const struct iwl_cfg iwl1000_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 1000 BGN", @@ -117,7 +118,8 @@ const struct iwl_cfg iwl1000_bg_cfg = { .eeprom_params = &iwl1000_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ .rx_with_siso_diversity = true, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .csr = &iwl_csr_v1 const struct iwl_cfg iwl100_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 100 BGN", diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/2000.c b/drivers/net/wireless/intel/iwlwifi/cfg/2000.c index 1b32ad413b9e..a63ca8820568 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/2000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/2000.c @@ -1,6 +1,7 @@ /****************************************************************************** * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -115,7 +116,8 @@ static const struct iwl_eeprom_params iwl20x0_eeprom_params = { .base_params = &iwl2000_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .csr = &iwl_csr_v1 const struct iwl_cfg iwl2000_2bgn_cfg = { @@ -142,7 +144,8 @@ const struct iwl_cfg iwl2000_2bgn_d_cfg = { .base_params = &iwl2030_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .csr = &iwl_csr_v1 const struct iwl_cfg iwl2030_2bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN", @@ -163,7 +166,8 @@ const struct iwl_cfg iwl2030_2bgn_cfg = { .eeprom_params = &iwl20x0_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ .rx_with_siso_diversity = true, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .csr = &iwl_csr_v1 const struct iwl_cfg iwl105_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 105 BGN", @@ -190,7 +194,8 @@ const struct iwl_cfg iwl105_bgn_d_cfg = { .eeprom_params = &iwl20x0_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ .rx_with_siso_diversity = true, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .csr = &iwl_csr_v1 const struct iwl_cfg iwl135_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 135 BGN", diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index dffd9df782b0..d4ba66aecdc9 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c @@ -54,7 +54,6 @@ #include <linux/module.h> #include <linux/stringify.h> #include "iwl-config.h" -#include "iwl-agn-hw.h" /* Highest firmware API version supported */ #define IWL_22000_UCODE_API_MAX 38 @@ -115,8 +114,6 @@ static const struct iwl_ht_params iwl_22000_ht_params = { .ucode_api_max = IWL_22000_UCODE_API_MAX, \ .ucode_api_min = IWL_22000_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_22000, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ .base_params = &iwl_22000_base_params, \ .led_mode = IWL_LED_RF_STATE, \ .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_22000, \ @@ -137,13 +134,13 @@ static const struct iwl_ht_params iwl_22000_ht_params = { .gen2 = true, \ .nvm_type = IWL_NVM_EXT, \ .dbgc_supported = true, \ - .tx_cmd_queue_size = 32, \ .min_umac_error_event_table = 0x400000 const struct iwl_cfg iwl22000_2ac_cfg_hr = { .name = "Intel(R) Dual Band Wireless AC 22000", .fw_name_pre = IWL_22000_HR_FW_PRE, IWL_DEVICE_22000, + .csr = &iwl_csr_v1, .ht_params = &iwl_22000_ht_params, .nvm_ver = IWL_22000_NVM_VERSION, .nvm_calib_ver = IWL_22000_TX_POWER_VERSION, @@ -154,6 +151,7 @@ const struct iwl_cfg iwl22000_2ac_cfg_hr_cdb = { .name = "Intel(R) Dual Band Wireless AC 22000", .fw_name_pre = IWL_22000_HR_CDB_FW_PRE, IWL_DEVICE_22000, + .csr = &iwl_csr_v1, .ht_params = &iwl_22000_ht_params, .nvm_ver = IWL_22000_NVM_VERSION, .nvm_calib_ver = IWL_22000_TX_POWER_VERSION, @@ -165,6 +163,7 @@ const struct iwl_cfg iwl22000_2ac_cfg_jf = { .name = "Intel(R) Dual Band Wireless AC 22000", .fw_name_pre = IWL_22000_JF_FW_PRE, IWL_DEVICE_22000, + .csr = &iwl_csr_v1, .ht_params = &iwl_22000_ht_params, .nvm_ver = IWL_22000_NVM_VERSION, .nvm_calib_ver = IWL_22000_TX_POWER_VERSION, @@ -175,6 +174,7 @@ const struct iwl_cfg iwl22000_2ax_cfg_hr = { .name = "Intel(R) Dual Band Wireless AX 22000", .fw_name_pre = IWL_22000_HR_FW_PRE, IWL_DEVICE_22000, + .csr = &iwl_csr_v1, .ht_params = &iwl_22000_ht_params, .nvm_ver = IWL_22000_NVM_VERSION, .nvm_calib_ver = IWL_22000_TX_POWER_VERSION, @@ -185,6 +185,7 @@ const struct iwl_cfg iwl22000_2ax_cfg_qnj_hr_f0 = { .name = "Intel(R) Dual Band Wireless AX 22000", .fw_name_pre = IWL_22000_HR_F0_FW_PRE, IWL_DEVICE_22000, + .csr = &iwl_csr_v1, .ht_params = &iwl_22000_ht_params, .nvm_ver = IWL_22000_NVM_VERSION, .nvm_calib_ver = IWL_22000_TX_POWER_VERSION, @@ -195,6 +196,7 @@ const struct iwl_cfg iwl22000_2ax_cfg_qnj_jf_b0 = { .name = "Intel(R) Dual Band Wireless AX 22000", .fw_name_pre = IWL_22000_JF_B0_FW_PRE, IWL_DEVICE_22000, + .csr = &iwl_csr_v1, .ht_params = &iwl_22000_ht_params, .nvm_ver = IWL_22000_NVM_VERSION, .nvm_calib_ver = IWL_22000_TX_POWER_VERSION, @@ -205,6 +207,7 @@ const struct iwl_cfg iwl22000_2ax_cfg_qnj_hr_a0 = { .name = "Intel(R) Dual Band Wireless AX 22000", .fw_name_pre = IWL_22000_HR_A0_FW_PRE, IWL_DEVICE_22000, + .csr = &iwl_csr_v1, .ht_params = &iwl_22000_ht_params, .nvm_ver = IWL_22000_NVM_VERSION, .nvm_calib_ver = IWL_22000_TX_POWER_VERSION, diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/5000.c b/drivers/net/wireless/intel/iwlwifi/cfg/5000.c index 4aa8f0a05c8a..a224f1be1ec2 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/5000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/5000.c @@ -1,6 +1,7 @@ /****************************************************************************** * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -28,7 +29,6 @@ #include <linux/stringify.h> #include "iwl-config.h" #include "iwl-agn-hw.h" -#include "iwl-csr.h" /* Highest firmware API version supported */ #define IWL5000_UCODE_API_MAX 5 @@ -89,7 +89,8 @@ static const struct iwl_eeprom_params iwl5000_eeprom_params = { .base_params = &iwl5000_base_params, \ .eeprom_params = &iwl5000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .csr = &iwl_csr_v1 const struct iwl_cfg iwl5300_agn_cfg = { .name = "Intel(R) Ultimate N WiFi Link 5300 AGN", @@ -153,7 +154,8 @@ const struct iwl_cfg iwl5350_agn_cfg = { .eeprom_params = &iwl5000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .csr = &iwl_csr_v1 const struct iwl_cfg iwl5150_agn_cfg = { .name = "Intel(R) WiMAX/WiFi Link 5150 AGN", diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/6000.c b/drivers/net/wireless/intel/iwlwifi/cfg/6000.c index 39335b7b0c16..dbcec7ce7863 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/6000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/6000.c @@ -1,6 +1,7 @@ /****************************************************************************** * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -135,7 +136,8 @@ static const struct iwl_eeprom_params iwl6000_eeprom_params = { .base_params = &iwl6000_g2_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .csr = &iwl_csr_v1 const struct iwl_cfg iwl6005_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6205 AGN", @@ -189,7 +191,8 @@ const struct iwl_cfg iwl6005_2agn_mow2_cfg = { .base_params = &iwl6000_g2_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .csr = &iwl_csr_v1 const struct iwl_cfg iwl6030_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN", @@ -225,7 +228,8 @@ const struct iwl_cfg iwl6030_2bg_cfg = { .base_params = &iwl6000_g2_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .csr = &iwl_csr_v1 const struct iwl_cfg iwl6035_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN", @@ -280,7 +284,8 @@ const struct iwl_cfg iwl130_bg_cfg = { .base_params = &iwl6000_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .csr = &iwl_csr_v1 const struct iwl_cfg iwl6000i_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6200 AGN", @@ -313,7 +318,8 @@ const struct iwl_cfg iwl6000i_2bg_cfg = { .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .csr = &iwl_csr_v1 const struct iwl_cfg iwl6050_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN", @@ -339,7 +345,8 @@ const struct iwl_cfg iwl6050_2abg_cfg = { .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .csr = &iwl_csr_v1 const struct iwl_cfg iwl6150_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN", @@ -366,6 +373,7 @@ const struct iwl_cfg iwl6000_3agn_cfg = { .eeprom_params = &iwl6000_eeprom_params, .ht_params = &iwl6000_ht_params, .led_mode = IWL_LED_BLINK, + .csr = &iwl_csr_v1, }; MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/7000.c b/drivers/net/wireless/intel/iwlwifi/cfg/7000.c index ce741beec1fc..69bfa827e82a 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/7000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/7000.c @@ -7,7 +7,8 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -34,7 +35,8 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -68,7 +70,6 @@ #include <linux/module.h> #include <linux/stringify.h> #include "iwl-config.h" -#include "iwl-agn-hw.h" /* Highest firmware API version supported */ #define IWL7260_UCODE_API_MAX 17 @@ -160,14 +161,13 @@ static const struct iwl_ht_params iwl7000_ht_params = { #define IWL_DEVICE_7000_COMMON \ .device_family = IWL_DEVICE_FAMILY_7000, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ .base_params = &iwl7000_base_params, \ .led_mode = IWL_LED_RF_STATE, \ .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000, \ .non_shared_ant = ANT_A, \ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ - .dccm_offset = IWL7000_DCCM_OFFSET + .dccm_offset = IWL7000_DCCM_OFFSET, \ + .csr = &iwl_csr_v1 #define IWL_DEVICE_7000 \ IWL_DEVICE_7000_COMMON, \ diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/8000.c b/drivers/net/wireless/intel/iwlwifi/cfg/8000.c index 3f4d9bac9f73..7262e973e0d6 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/8000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/8000.c @@ -7,7 +7,8 @@ * * Copyright(c) 2014 Intel Corporation. All rights reserved. * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -34,6 +35,7 @@ * * Copyright(c) 2014 Intel Corporation. All rights reserved. * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -67,7 +69,6 @@ #include <linux/module.h> #include <linux/stringify.h> #include "iwl-config.h" -#include "iwl-agn-hw.h" /* Highest firmware API version supported */ #define IWL8000_UCODE_API_MAX 36 @@ -140,8 +141,6 @@ static const struct iwl_tt_params iwl8000_tt_params = { #define IWL_DEVICE_8000_COMMON \ .device_family = IWL_DEVICE_FAMILY_8000, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ .base_params = &iwl8000_base_params, \ .led_mode = IWL_LED_RF_STATE, \ .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000, \ @@ -158,7 +157,8 @@ static const struct iwl_tt_params iwl8000_tt_params = { .apmg_not_supported = true, \ .nvm_type = IWL_NVM_EXT, \ .dbgc_supported = true, \ - .min_umac_error_event_table = 0x800000 + .min_umac_error_event_table = 0x800000, \ + .csr = &iwl_csr_v1 #define IWL_DEVICE_8000 \ IWL_DEVICE_8000_COMMON, \ diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c index e1c869a1f8cc..e20c30b29c03 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c @@ -54,7 +54,6 @@ #include <linux/module.h> #include <linux/stringify.h> #include "iwl-config.h" -#include "iwl-agn-hw.h" #include "fw/file.h" /* Highest firmware API version supported */ @@ -135,12 +134,10 @@ static const struct iwl_tt_params iwl9000_tt_params = { .ucode_api_max = IWL9000_UCODE_API_MAX, \ .ucode_api_min = IWL9000_UCODE_API_MIN, \ .device_family = IWL_DEVICE_FAMILY_9000, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ .base_params = &iwl9000_base_params, \ .led_mode = IWL_LED_RF_STATE, \ .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_9000, \ - .non_shared_ant = ANT_A, \ + .non_shared_ant = ANT_B, \ .dccm_offset = IWL9000_DCCM_OFFSET, \ .dccm_len = IWL9000_DCCM_LEN, \ .dccm2_offset = IWL9000_DCCM2_OFFSET, \ @@ -156,7 +153,8 @@ static const struct iwl_tt_params iwl9000_tt_params = { .rf_id = true, \ .nvm_type = IWL_NVM_EXT, \ .dbgc_supported = true, \ - .min_umac_error_event_table = 0x800000 + .min_umac_error_event_table = 0x800000, \ + .csr = &iwl_csr_v1 const struct iwl_cfg iwl9160_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 9160", diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c index e68254e12764..030482b357a3 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c @@ -1200,16 +1200,16 @@ static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) return -EINVAL; } - if (!data->sku_cap_11n_enable && !data->sku_cap_band_24GHz_enable && - !data->sku_cap_band_52GHz_enable) { + if (!data->sku_cap_11n_enable && !data->sku_cap_band_24ghz_enable && + !data->sku_cap_band_52ghz_enable) { IWL_ERR(priv, "Invalid device sku\n"); return -EINVAL; } IWL_DEBUG_INFO(priv, "Device SKU: 24GHz %s %s, 52GHz %s %s, 11.n %s %s\n", - data->sku_cap_band_24GHz_enable ? "" : "NOT", "enabled", - data->sku_cap_band_52GHz_enable ? "" : "NOT", "enabled", + data->sku_cap_band_24ghz_enable ? "" : "NOT", "enabled", + data->sku_cap_band_52ghz_enable ? "" : "NOT", "enabled", data->sku_cap_11n_enable ? "" : "NOT", "enabled"); priv->hw_params.tx_chains_num = diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h index a57c7223df0f..5f6e855006dd 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h @@ -88,11 +88,6 @@ enum iwl_data_path_subcmd_ids { TLC_MNG_CONFIG_CMD = 0xF, /** - * @TLC_MNG_NOTIF_REQ_CMD: &struct iwl_tlc_notif_req_config_cmd - */ - TLC_MNG_NOTIF_REQ_CMD = 0x10, - - /** * @TLC_MNG_UPDATE_NOTIF: &struct iwl_tlc_update_notif */ TLC_MNG_UPDATE_NOTIF = 0xF7, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h index 37c57bcbfb4a..8d6dc9189985 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h @@ -190,22 +190,36 @@ struct iwl_nvm_get_info_general { } __packed; /* GRP_REGULATORY_NVM_GET_INFO_GENERAL_S_VER_1 */ /** + * enum iwl_nvm_mac_sku_flags - flags in &iwl_nvm_get_info_sku + * @NVM_MAC_SKU_FLAGS_BAND_2_4_ENABLED: true if 2.4 band enabled + * @NVM_MAC_SKU_FLAGS_BAND_5_2_ENABLED: true if 5.2 band enabled + * @NVM_MAC_SKU_FLAGS_802_11N_ENABLED: true if 11n enabled + * @NVM_MAC_SKU_FLAGS_802_11AC_ENABLED: true if 11ac enabled + * @NVM_MAC_SKU_FLAGS_802_11AX_ENABLED: true if 11ax enabled + * @NVM_MAC_SKU_FLAGS_MIMO_DISABLED: true if MIMO disabled + * @NVM_MAC_SKU_FLAGS_WAPI_ENABLED: true if WAPI enabled + * @NVM_MAC_SKU_FLAGS_REG_CHECK_ENABLED: true if regulatory checker enabled + * @NVM_MAC_SKU_FLAGS_API_LOCK_ENABLED: true if API lock enabled + */ +enum iwl_nvm_mac_sku_flags { + NVM_MAC_SKU_FLAGS_BAND_2_4_ENABLED = BIT(0), + NVM_MAC_SKU_FLAGS_BAND_5_2_ENABLED = BIT(1), + NVM_MAC_SKU_FLAGS_802_11N_ENABLED = BIT(2), + NVM_MAC_SKU_FLAGS_802_11AC_ENABLED = BIT(3), + NVM_MAC_SKU_FLAGS_802_11AX_ENABLED = BIT(4), + NVM_MAC_SKU_FLAGS_MIMO_DISABLED = BIT(5), + NVM_MAC_SKU_FLAGS_WAPI_ENABLED = BIT(8), + NVM_MAC_SKU_FLAGS_REG_CHECK_ENABLED = BIT(14), + NVM_MAC_SKU_FLAGS_API_LOCK_ENABLED = BIT(15), +}; + +/** * struct iwl_nvm_get_info_sku - mac information - * @enable_24g: band 2.4G enabled - * @enable_5g: band 5G enabled - * @enable_11n: 11n enabled - * @enable_11ac: 11ac enabled - * @mimo_disable: MIMO enabled - * @ext_crypto: Extended crypto enabled + * @mac_sku_flags: flags for SKU, see &enum iwl_nvm_mac_sku_flags */ struct iwl_nvm_get_info_sku { - __le32 enable_24g; - __le32 enable_5g; - __le32 enable_11n; - __le32 enable_11ac; - __le32 mimo_disable; - __le32 ext_crypto; -} __packed; /* GRP_REGULATORY_NVM_GET_INFO_MAC_SKU_SECTION_S_VER_1 */ + __le32 mac_sku_flags; +} __packed; /* REGULATORY_NVM_GET_INFO_MAC_SKU_SECTION_S_VER_2 */ /** * struct iwl_nvm_get_info_phy - phy information @@ -243,7 +257,7 @@ struct iwl_nvm_get_info_rsp { struct iwl_nvm_get_info_sku mac_sku; struct iwl_nvm_get_info_phy phy_sku; struct iwl_nvm_get_info_regulatory regulatory; -} __packed; /* GRP_REGULATORY_NVM_GET_INFO_CMD_RSP_S_VER_1 */ +} __packed; /* GRP_REGULATORY_NVM_GET_INFO_CMD_RSP_S_VER_2 */ /** * struct iwl_nvm_access_complete_cmd - NVM_ACCESS commands are completed diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h index e49a6f7be613..21e13a315421 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -28,6 +29,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -64,62 +66,38 @@ /** * enum iwl_tlc_mng_cfg_flags_enum - options for TLC config flags - * @IWL_TLC_MNG_CFG_FLAGS_CCK_MSK: CCK support - * @IWL_TLC_MNG_CFG_FLAGS_DD_MSK: enable DD * @IWL_TLC_MNG_CFG_FLAGS_STBC_MSK: enable STBC * @IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK: enable LDPC - * @IWL_TLC_MNG_CFG_FLAGS_BF_MSK: enable BFER - * @IWL_TLC_MNG_CFG_FLAGS_DCM_MSK: enable DCM */ enum iwl_tlc_mng_cfg_flags { - IWL_TLC_MNG_CFG_FLAGS_CCK_MSK = BIT(0), - IWL_TLC_MNG_CFG_FLAGS_DD_MSK = BIT(1), - IWL_TLC_MNG_CFG_FLAGS_STBC_MSK = BIT(2), - IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK = BIT(3), - IWL_TLC_MNG_CFG_FLAGS_BF_MSK = BIT(4), - IWL_TLC_MNG_CFG_FLAGS_DCM_MSK = BIT(5), + IWL_TLC_MNG_CFG_FLAGS_STBC_MSK = BIT(0), + IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK = BIT(1), }; /** * enum iwl_tlc_mng_cfg_cw - channel width options - * @IWL_TLC_MNG_MAX_CH_WIDTH_20MHZ: 20MHZ channel - * @IWL_TLC_MNG_MAX_CH_WIDTH_40MHZ: 40MHZ channel - * @IWL_TLC_MNG_MAX_CH_WIDTH_80MHZ: 80MHZ channel - * @IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ: 160MHZ channel - * @IWL_TLC_MNG_MAX_CH_WIDTH_LAST: maximum value + * @IWL_TLC_MNG_CH_WIDTH_20MHZ: 20MHZ channel + * @IWL_TLC_MNG_CH_WIDTH_40MHZ: 40MHZ channel + * @IWL_TLC_MNG_CH_WIDTH_80MHZ: 80MHZ channel + * @IWL_TLC_MNG_CH_WIDTH_160MHZ: 160MHZ channel + * @IWL_TLC_MNG_CH_WIDTH_LAST: maximum value */ enum iwl_tlc_mng_cfg_cw { - IWL_TLC_MNG_MAX_CH_WIDTH_20MHZ, - IWL_TLC_MNG_MAX_CH_WIDTH_40MHZ, - IWL_TLC_MNG_MAX_CH_WIDTH_80MHZ, - IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ, - IWL_TLC_MNG_MAX_CH_WIDTH_LAST = IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ, + IWL_TLC_MNG_CH_WIDTH_20MHZ, + IWL_TLC_MNG_CH_WIDTH_40MHZ, + IWL_TLC_MNG_CH_WIDTH_80MHZ, + IWL_TLC_MNG_CH_WIDTH_160MHZ, + IWL_TLC_MNG_CH_WIDTH_LAST = IWL_TLC_MNG_CH_WIDTH_160MHZ, }; /** * enum iwl_tlc_mng_cfg_chains - possible chains * @IWL_TLC_MNG_CHAIN_A_MSK: chain A * @IWL_TLC_MNG_CHAIN_B_MSK: chain B - * @IWL_TLC_MNG_CHAIN_C_MSK: chain C */ enum iwl_tlc_mng_cfg_chains { IWL_TLC_MNG_CHAIN_A_MSK = BIT(0), IWL_TLC_MNG_CHAIN_B_MSK = BIT(1), - IWL_TLC_MNG_CHAIN_C_MSK = BIT(2), -}; - -/** - * enum iwl_tlc_mng_cfg_gi - guard interval options - * @IWL_TLC_MNG_SGI_20MHZ_MSK: enable short GI for 20MHZ - * @IWL_TLC_MNG_SGI_40MHZ_MSK: enable short GI for 40MHZ - * @IWL_TLC_MNG_SGI_80MHZ_MSK: enable short GI for 80MHZ - * @IWL_TLC_MNG_SGI_160MHZ_MSK: enable short GI for 160MHZ - */ -enum iwl_tlc_mng_cfg_gi { - IWL_TLC_MNG_SGI_20MHZ_MSK = BIT(0), - IWL_TLC_MNG_SGI_40MHZ_MSK = BIT(1), - IWL_TLC_MNG_SGI_80MHZ_MSK = BIT(2), - IWL_TLC_MNG_SGI_160MHZ_MSK = BIT(3), }; /** @@ -145,25 +123,7 @@ enum iwl_tlc_mng_cfg_mode { }; /** - * enum iwl_tlc_mng_vht_he_types - VHT HE types - * @IWL_TLC_MNG_VALID_VHT_HE_TYPES_SU: VHT HT single user - * @IWL_TLC_MNG_VALID_VHT_HE_TYPES_SU_EXT: VHT HT single user extended - * @IWL_TLC_MNG_VALID_VHT_HE_TYPES_MU: VHT HT multiple users - * @IWL_TLC_MNG_VALID_VHT_HE_TYPES_TRIG_BASED: trigger based - * @IWL_TLC_MNG_VALID_VHT_HE_TYPES_NUM: a count of possible types - */ -enum iwl_tlc_mng_vht_he_types { - IWL_TLC_MNG_VALID_VHT_HE_TYPES_SU = 0, - IWL_TLC_MNG_VALID_VHT_HE_TYPES_SU_EXT, - IWL_TLC_MNG_VALID_VHT_HE_TYPES_MU, - IWL_TLC_MNG_VALID_VHT_HE_TYPES_TRIG_BASED, - IWL_TLC_MNG_VALID_VHT_HE_TYPES_NUM = - IWL_TLC_MNG_VALID_VHT_HE_TYPES_TRIG_BASED, - -}; - -/** - * enum iwl_tlc_mng_ht_rates - HT/VHT rates + * enum iwl_tlc_mng_ht_rates - HT/VHT/HE rates * @IWL_TLC_MNG_HT_RATE_MCS0: index of MCS0 * @IWL_TLC_MNG_HT_RATE_MCS1: index of MCS1 * @IWL_TLC_MNG_HT_RATE_MCS2: index of MCS2 @@ -174,6 +134,8 @@ enum iwl_tlc_mng_vht_he_types { * @IWL_TLC_MNG_HT_RATE_MCS7: index of MCS7 * @IWL_TLC_MNG_HT_RATE_MCS8: index of MCS8 * @IWL_TLC_MNG_HT_RATE_MCS9: index of MCS9 + * @IWL_TLC_MNG_HT_RATE_MCS10: index of MCS10 + * @IWL_TLC_MNG_HT_RATE_MCS11: index of MCS11 * @IWL_TLC_MNG_HT_RATE_MAX: maximal rate for HT/VHT */ enum iwl_tlc_mng_ht_rates { @@ -187,81 +149,73 @@ enum iwl_tlc_mng_ht_rates { IWL_TLC_MNG_HT_RATE_MCS7, IWL_TLC_MNG_HT_RATE_MCS8, IWL_TLC_MNG_HT_RATE_MCS9, - IWL_TLC_MNG_HT_RATE_MAX = IWL_TLC_MNG_HT_RATE_MCS9, + IWL_TLC_MNG_HT_RATE_MCS10, + IWL_TLC_MNG_HT_RATE_MCS11, + IWL_TLC_MNG_HT_RATE_MAX = IWL_TLC_MNG_HT_RATE_MCS11, }; /* Maximum supported tx antennas number */ -#define MAX_RS_ANT_NUM 3 +#define MAX_NSS 2 /** * struct tlc_config_cmd - TLC configuration * @sta_id: station id * @reserved1: reserved - * @max_supp_ch_width: channel width - * @flags: bitmask of &enum iwl_tlc_mng_cfg_flags - * @chains: bitmask of &enum iwl_tlc_mng_cfg_chains - * @max_supp_ss: valid values are 0-3, 0 - spatial streams are not supported - * @valid_vht_he_types: bitmap of &enum iwl_tlc_mng_vht_he_types - * @non_ht_supp_rates: bitmap of supported legacy rates - * @ht_supp_rates: bitmap of supported HT/VHT rates, valid bits are 0-9 + * @max_ch_width: max supported channel width from @enum iwl_tlc_mng_cfg_cw * @mode: &enum iwl_tlc_mng_cfg_mode - * @reserved2: reserved - * @he_supp_rates: bitmap of supported HE rates + * @chains: bitmask of &enum iwl_tlc_mng_cfg_chains + * @amsdu: TX amsdu is supported + * @flags: bitmask of &enum iwl_tlc_mng_cfg_flags + * @non_ht_rates: bitmap of supported legacy rates + * @ht_rates: bitmap of &enum iwl_tlc_mng_ht_rates, per <nss, channel-width> + * pair (0 - 80mhz width and below, 1 - 160mhz). + * @max_mpdu_len: max MPDU length, in bytes * @sgi_ch_width_supp: bitmap of SGI support per channel width - * @he_gi_support: 11ax HE guard interval - * @max_ampdu_cnt: max AMPDU size (frames count) + * use BIT(@enum iwl_tlc_mng_cfg_cw) + * @reserved2: reserved */ struct iwl_tlc_config_cmd { u8 sta_id; u8 reserved1[3]; - u8 max_supp_ch_width; + u8 max_ch_width; + u8 mode; u8 chains; - u8 max_supp_ss; - u8 valid_vht_he_types; + u8 amsdu; __le16 flags; - __le16 non_ht_supp_rates; - __le16 ht_supp_rates[MAX_RS_ANT_NUM]; - u8 mode; - u8 reserved2; - __le16 he_supp_rates; + __le16 non_ht_rates; + __le16 ht_rates[MAX_NSS][2]; + __le16 max_mpdu_len; u8 sgi_ch_width_supp; - u8 he_gi_support; - __le32 max_ampdu_cnt; -} __packed; /* TLC_MNG_CONFIG_CMD_API_S_VER_1 */ - -#define IWL_TLC_NOTIF_INIT_RATE_POS 0 -#define IWL_TLC_NOTIF_INIT_RATE_MSK BIT(IWL_TLC_NOTIF_INIT_RATE_POS) -#define IWL_TLC_NOTIF_REQ_INTERVAL (500) + u8 reserved2[1]; +} __packed; /* TLC_MNG_CONFIG_CMD_API_S_VER_2 */ /** - * struct iwl_tlc_notif_req_config_cmd - request notif on specific changes - * @sta_id: relevant station - * @reserved1: reserved - * @flags: bitmap of requested notifications %IWL_TLC_NOTIF_INIT_\* - * @interval: minimum time between notifications from TLC to the driver (msec) - * @reserved2: reserved + * enum iwl_tlc_update_flags - updated fields + * @IWL_TLC_NOTIF_FLAG_RATE: last initial rate update + * @IWL_TLC_NOTIF_FLAG_AMSDU: umsdu parameters update */ -struct iwl_tlc_notif_req_config_cmd { - u8 sta_id; - u8 reserved1; - __le16 flags; - __le16 interval; - __le16 reserved2; -} __packed; /* TLC_MNG_NOTIF_REQ_CMD_API_S_VER_1 */ +enum iwl_tlc_update_flags { + IWL_TLC_NOTIF_FLAG_RATE = BIT(0), + IWL_TLC_NOTIF_FLAG_AMSDU = BIT(1), +}; /** * struct iwl_tlc_update_notif - TLC notification from FW * @sta_id: station id * @reserved: reserved * @flags: bitmap of notifications reported - * @values: field per flag in struct iwl_tlc_notif_req_config_cmd + * @rate: current initial rate + * @amsdu_size: Max AMSDU size, in bytes + * @amsdu_enabled: bitmap for per-TID AMSDU enablement */ struct iwl_tlc_update_notif { u8 sta_id; - u8 reserved; - __le16 flags; - __le32 values[16]; -} __packed; /* TLC_MNG_UPDATE_NTFY_API_S_VER_1 */ + u8 reserved[3]; + __le32 flags; + __le32 rate; + __le32 amsdu_size; + __le32 amsdu_enabled; +} __packed; /* TLC_MNG_UPDATE_NTFY_API_S_VER_2 */ /** * enum iwl_tlc_debug_flags - debug options diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h index e7565f37ece9..7e570c4a9df0 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h @@ -295,6 +295,7 @@ enum iwl_rx_mpdu_status { IWL_RX_MPDU_STATUS_MIC_OK = BIT(6), IWL_RX_MPDU_RES_STATUS_TTAK_OK = BIT(7), IWL_RX_MPDU_STATUS_SEC_MASK = 0x7 << 8, + IWL_RX_MPDU_STATUS_SEC_UNKNOWN = IWL_RX_MPDU_STATUS_SEC_MASK, IWL_RX_MPDU_STATUS_SEC_NONE = 0x0 << 8, IWL_RX_MPDU_STATUS_SEC_WEP = 0x1 << 8, IWL_RX_MPDU_STATUS_SEC_CCM = 0x2 << 8, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h b/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h index dfa111bb411e..6ac240b6eace 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h @@ -131,6 +131,8 @@ enum iwl_tx_queue_cfg_actions { TX_QUEUE_CFG_TFD_SHORT_FORMAT = BIT(1), }; +#define IWL_DEFAULT_QUEUE_SIZE 256 +#define IWL_MGMT_QUEUE_SIZE 16 /** * struct iwl_tx_queue_cfg_cmd - txq hw scheduler config command * @sta_id: station id diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h index 72259bff9922..507d9a49fa97 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h @@ -227,4 +227,40 @@ static inline void iwl_fw_cancel_dump(struct iwl_fw_runtime *fwrt) cancel_delayed_work_sync(&fwrt->dump.wk); } +#ifdef CONFIG_IWLWIFI_DEBUGFS +static inline void iwl_fw_cancel_timestamp(struct iwl_fw_runtime *fwrt) +{ + fwrt->timestamp.delay = 0; + cancel_delayed_work_sync(&fwrt->timestamp.wk); +} + +void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay); + +static inline void iwl_fw_suspend_timestamp(struct iwl_fw_runtime *fwrt) +{ + cancel_delayed_work_sync(&fwrt->timestamp.wk); +} + +static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) +{ + if (!fwrt->timestamp.delay) + return; + + schedule_delayed_work(&fwrt->timestamp.wk, + round_jiffies_relative(fwrt->timestamp.delay)); +} + +#else + +static inline void iwl_fw_cancel_timestamp(struct iwl_fw_runtime *fwrt) {} + +static inline void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, + u32 delay) {} + +static inline void iwl_fw_suspend_timestamp(struct iwl_fw_runtime *fwrt) {} + +static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) {} + +#endif /* CONFIG_IWLWIFI_DEBUGFS */ + #endif /* __iwl_fw_dbg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c index 8f005cd69559..8ba5a60ec9ed 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c @@ -64,6 +64,7 @@ *****************************************************************************/ #include "api/commands.h" #include "debugfs.h" +#include "dbg.h" #define FWRT_DEBUGFS_READ_FILE_OPS(name) \ static ssize_t iwl_dbgfs_##name##_read(struct iwl_fw_runtime *fwrt, \ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.h b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.h index d93f6a4bb22d..cbbfa8e9e66d 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.h @@ -69,28 +69,6 @@ int iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt, struct dentry *dbgfs_dir); -static inline void iwl_fw_cancel_timestamp(struct iwl_fw_runtime *fwrt) -{ - fwrt->timestamp.delay = 0; - cancel_delayed_work_sync(&fwrt->timestamp.wk); -} - -static inline void iwl_fw_suspend_timestamp(struct iwl_fw_runtime *fwrt) -{ - cancel_delayed_work_sync(&fwrt->timestamp.wk); -} - -static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) -{ - if (!fwrt->timestamp.delay) - return; - - schedule_delayed_work(&fwrt->timestamp.wk, - round_jiffies_relative(fwrt->timestamp.delay)); -} - -void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay); - #else static inline int iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt, struct dentry *dbgfs_dir) @@ -98,13 +76,4 @@ static inline int iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt, return 0; } -static inline void iwl_fw_cancel_timestamp(struct iwl_fw_runtime *fwrt) {} - -static inline void iwl_fw_suspend_timestamp(struct iwl_fw_runtime *fwrt) {} - -static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) {} - -static inline void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, - u32 delay) {} - #endif /* CONFIG_IWLWIFI_DEBUGFS */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index 9b2805e1e3b1..9d939cbaf6c6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -8,6 +8,7 @@ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -35,6 +36,7 @@ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -143,6 +145,7 @@ enum iwl_ucode_tlv_type { IWL_UCODE_TLV_FW_DBG_TRIGGER = 40, IWL_UCODE_TLV_FW_GSCAN_CAPA = 50, IWL_UCODE_TLV_FW_MEM_SEG = 51, + IWL_UCODE_TLV_IML = 52, }; struct iwl_ucode_tlv { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.h b/drivers/net/wireless/intel/iwlwifi/fw/img.h index b23ffe12ad84..f4912382b6af 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/img.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/img.h @@ -8,6 +8,7 @@ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -35,6 +36,7 @@ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -241,6 +243,8 @@ enum iwl_fw_type { * @ucode_ver: ucode version from the ucode file * @fw_version: firmware version string * @img: ucode image like ucode_rt, ucode_init, ucode_wowlan. + * @iml_len: length of the image loader image + * @iml: image loader fw image * @ucode_capa: capabilities parsed from the ucode file. * @enhance_sensitivity_table: device can do enhanced sensitivity. * @init_evtlog_ptr: event log offset for init ucode. @@ -267,6 +271,8 @@ struct iwl_fw { /* ucode images */ struct fw_img img[IWL_UCODE_TYPE_MAX]; + size_t iml_len; + u8 *iml; struct iwl_ucode_capabilities ucode_capa; bool enhance_sensitivity_table; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/nvm.c b/drivers/net/wireless/intel/iwlwifi/fw/nvm.c deleted file mode 100644 index bd2e1fb43f5a..000000000000 --- a/drivers/net/wireless/intel/iwlwifi/fw/nvm.c +++ /dev/null @@ -1,162 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless <linuxwifi@intel.com> - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include "iwl-drv.h" -#include "runtime.h" -#include "fw/api/nvm-reg.h" -#include "fw/api/commands.h" -#include "iwl-nvm-parse.h" - -struct iwl_nvm_data *iwl_fw_get_nvm(struct iwl_fw_runtime *fwrt) -{ - struct iwl_nvm_get_info cmd = {}; - struct iwl_nvm_get_info_rsp *rsp; - struct iwl_trans *trans = fwrt->trans; - struct iwl_nvm_data *nvm; - struct iwl_host_cmd hcmd = { - .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, - .data = { &cmd, }, - .len = { sizeof(cmd) }, - .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, NVM_GET_INFO) - }; - int ret; - bool lar_fw_supported = !iwlwifi_mod_params.lar_disable && - fw_has_capa(&fwrt->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_LAR_SUPPORT); - - ret = iwl_trans_send_cmd(trans, &hcmd); - if (ret) - return ERR_PTR(ret); - - if (WARN(iwl_rx_packet_payload_len(hcmd.resp_pkt) != sizeof(*rsp), - "Invalid payload len in NVM response from FW %d", - iwl_rx_packet_payload_len(hcmd.resp_pkt))) { - ret = -EINVAL; - goto out; - } - - rsp = (void *)hcmd.resp_pkt->data; - if (le32_to_cpu(rsp->general.flags) & NVM_GENERAL_FLAGS_EMPTY_OTP) - IWL_INFO(fwrt, "OTP is empty\n"); - - nvm = kzalloc(sizeof(*nvm) + - sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS, - GFP_KERNEL); - if (!nvm) { - ret = -ENOMEM; - goto out; - } - - iwl_set_hw_address_from_csr(trans, nvm); - /* TODO: if platform NVM has MAC address - override it here */ - - if (!is_valid_ether_addr(nvm->hw_addr)) { - IWL_ERR(fwrt, "no valid mac address was found\n"); - ret = -EINVAL; - goto err_free; - } - - IWL_INFO(trans, "base HW address: %pM\n", nvm->hw_addr); - - /* Initialize general data */ - nvm->nvm_version = le16_to_cpu(rsp->general.nvm_version); - - /* Initialize MAC sku data */ - nvm->sku_cap_11ac_enable = - le32_to_cpu(rsp->mac_sku.enable_11ac); - nvm->sku_cap_11n_enable = - le32_to_cpu(rsp->mac_sku.enable_11n); - nvm->sku_cap_band_24GHz_enable = - le32_to_cpu(rsp->mac_sku.enable_24g); - nvm->sku_cap_band_52GHz_enable = - le32_to_cpu(rsp->mac_sku.enable_5g); - nvm->sku_cap_mimo_disabled = - le32_to_cpu(rsp->mac_sku.mimo_disable); - - /* Initialize PHY sku data */ - nvm->valid_tx_ant = (u8)le32_to_cpu(rsp->phy_sku.tx_chains); - nvm->valid_rx_ant = (u8)le32_to_cpu(rsp->phy_sku.rx_chains); - - /* Initialize regulatory data */ - nvm->lar_enabled = - le32_to_cpu(rsp->regulatory.lar_enabled) && lar_fw_supported; - - iwl_init_sbands(trans->dev, trans->cfg, nvm, - rsp->regulatory.channel_profile, - nvm->valid_tx_ant & fwrt->fw->valid_tx_ant, - nvm->valid_rx_ant & fwrt->fw->valid_rx_ant, - nvm->lar_enabled, false); - - iwl_free_resp(&hcmd); - return nvm; - -err_free: - kfree(nvm); -out: - iwl_free_resp(&hcmd); - return ERR_PTR(ret); -} -IWL_EXPORT_SYMBOL(iwl_fw_get_nvm); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/paging.c b/drivers/net/wireless/intel/iwlwifi/fw/paging.c index 1fec8e3a6b35..9b8dd7fe7112 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/paging.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/paging.c @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,6 +31,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -163,7 +165,7 @@ static int iwl_alloc_fw_paging_mem(struct iwl_fw_runtime *fwrt, static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt, const struct fw_img *image) { - int sec_idx, idx; + int sec_idx, idx, ret; u32 offset = 0; /* @@ -190,17 +192,23 @@ static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt, */ if (sec_idx >= image->num_sec - 1) { IWL_ERR(fwrt, "Paging: Missing CSS and/or paging sections\n"); - iwl_free_fw_paging(fwrt); - return -EINVAL; + ret = -EINVAL; + goto err; } /* copy the CSS block to the dram */ IWL_DEBUG_FW(fwrt, "Paging: load paging CSS to FW, sec = %d\n", sec_idx); + if (image->sec[sec_idx].len > fwrt->fw_paging_db[0].fw_paging_size) { + IWL_ERR(fwrt, "CSS block is larger than paging size\n"); + ret = -EINVAL; + goto err; + } + memcpy(page_address(fwrt->fw_paging_db[0].fw_paging_block), image->sec[sec_idx].data, - fwrt->fw_paging_db[0].fw_paging_size); + image->sec[sec_idx].len); dma_sync_single_for_device(fwrt->trans->dev, fwrt->fw_paging_db[0].fw_paging_phys, fwrt->fw_paging_db[0].fw_paging_size, @@ -213,17 +221,39 @@ static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt, sec_idx++; /* - * copy the paging blocks to the dram - * loop index start from 1 since that CSS block already copied to dram - * and CSS index is 0. - * loop stop at num_of_paging_blk since that last block is not full. + * Copy the paging blocks to the dram. The loop index starts + * from 1 since the CSS block (index 0) was already copied to + * dram. We use num_of_paging_blk + 1 to account for that. */ - for (idx = 1; idx < fwrt->num_of_paging_blk; idx++) { + for (idx = 1; idx < fwrt->num_of_paging_blk + 1; idx++) { struct iwl_fw_paging *block = &fwrt->fw_paging_db[idx]; + int remaining = image->sec[sec_idx].len - offset; + int len = block->fw_paging_size; + + /* + * For the last block, we copy all that is remaining, + * for all other blocks, we copy fw_paging_size at a + * time. */ + if (idx == fwrt->num_of_paging_blk) { + len = remaining; + if (remaining != + fwrt->num_of_pages_in_last_blk * FW_PAGING_SIZE) { + IWL_ERR(fwrt, + "Paging: last block contains more data than expected %d\n", + remaining); + ret = -EINVAL; + goto err; + } + } else if (block->fw_paging_size > remaining) { + IWL_ERR(fwrt, + "Paging: not enough data in other in block %d (%d)\n", + idx, remaining); + ret = -EINVAL; + goto err; + } memcpy(page_address(block->fw_paging_block), - image->sec[sec_idx].data + offset, - block->fw_paging_size); + image->sec[sec_idx].data + offset, len); dma_sync_single_for_device(fwrt->trans->dev, block->fw_paging_phys, block->fw_paging_size, @@ -231,30 +261,16 @@ static int iwl_fill_paging_mem(struct iwl_fw_runtime *fwrt, IWL_DEBUG_FW(fwrt, "Paging: copied %d paging bytes to block %d\n", - fwrt->fw_paging_db[idx].fw_paging_size, - idx); - - offset += fwrt->fw_paging_db[idx].fw_paging_size; - } - - /* copy the last paging block */ - if (fwrt->num_of_pages_in_last_blk > 0) { - struct iwl_fw_paging *block = &fwrt->fw_paging_db[idx]; + len, idx); - memcpy(page_address(block->fw_paging_block), - image->sec[sec_idx].data + offset, - FW_PAGING_SIZE * fwrt->num_of_pages_in_last_blk); - dma_sync_single_for_device(fwrt->trans->dev, - block->fw_paging_phys, - block->fw_paging_size, - DMA_BIDIRECTIONAL); - - IWL_DEBUG_FW(fwrt, - "Paging: copied %d pages in the last block %d\n", - fwrt->num_of_pages_in_last_blk, idx); + offset += block->fw_paging_size; } return 0; + +err: + iwl_free_fw_paging(fwrt); + return ret; } static int iwl_save_fw_paging(struct iwl_fw_runtime *fwrt, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 3fb940ebd74a..d8db1dd100b0 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -170,6 +170,5 @@ void iwl_get_shared_mem_conf(struct iwl_fw_runtime *fwrt); void iwl_fwrt_handle_notification(struct iwl_fw_runtime *fwrt, struct iwl_rx_cmd_buffer *rxb); -struct iwl_nvm_data *iwl_fw_get_nvm(struct iwl_fw_runtime *fwrt); #endif /* __iwl_fw_runtime_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index f0f5636dd3ea..c503b26793f6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -7,6 +7,7 @@ * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. * Copyright (C) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright (C) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -69,6 +71,7 @@ #include <linux/netdevice.h> #include <linux/ieee80211.h> #include <linux/nl80211.h> +#include "iwl-csr.h" enum iwl_device_family { IWL_DEVICE_FAMILY_UNDEFINED, @@ -151,6 +154,8 @@ enum iwl_nvm_type { #define ANT_AC (ANT_A | ANT_C) #define ANT_BC (ANT_B | ANT_C) #define ANT_ABC (ANT_A | ANT_B | ANT_C) +#define MAX_ANT_NUM 3 + static inline u8 num_of_ant(u8 mask) { @@ -283,6 +288,52 @@ struct iwl_pwr_tx_backoff { }; /** + * struct iwl_csr_params + * + * @flag_sw_reset: reset the device + * @flag_mac_clock_ready: + * Indicates MAC (ucode processor, etc.) is powered up and can run. + * Internal resources are accessible. + * NOTE: This does not indicate that the processor is actually running. + * NOTE: This does not indicate that device has completed + * init or post-power-down restore of internal SRAM memory. + * Use CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that + * SRAM is restored and uCode is in normal operation mode. + * This note is relevant only for pre 5xxx devices. + * NOTE: After device reset, this bit remains "0" until host sets + * INIT_DONE + * @flag_init_done: Host sets this to put device into fully operational + * D0 power mode. Host resets this after SW_RESET to put device into + * low power mode. + * @flag_mac_access_req: Host sets this to request and maintain MAC wakeup, + * to allow host access to device-internal resources. Host must wait for + * mac_clock_ready (and !GOING_TO_SLEEP) before accessing non-CSR device + * registers. + * @flag_val_mac_access_en: mac access is enabled + * @flag_master_dis: disable master + * @flag_stop_master: stop master + * @addr_sw_reset: address for resetting the device + * @mac_addr0_otp: first part of MAC address from OTP + * @mac_addr1_otp: second part of MAC address from OTP + * @mac_addr0_strap: first part of MAC address from strap + * @mac_addr1_strap: second part of MAC address from strap + */ +struct iwl_csr_params { + u8 flag_sw_reset; + u8 flag_mac_clock_ready; + u8 flag_init_done; + u8 flag_mac_access_req; + u8 flag_val_mac_access_en; + u8 flag_master_dis; + u8 flag_stop_master; + u8 addr_sw_reset; + u32 mac_addr0_otp; + u32 mac_addr1_otp; + u32 mac_addr0_strap; + u32 mac_addr1_strap; +}; + +/** * struct iwl_cfg * @name: Official name of the device * @fw_name_pre: Firmware filename prefix. The api version and extension @@ -294,8 +345,8 @@ struct iwl_pwr_tx_backoff { * next step. Supported only in integrated solutions. * @ucode_api_max: Highest version of uCode API supported by driver. * @ucode_api_min: Lowest version of uCode API supported by driver. - * @max_inst_size: The maximal length of the fw inst section - * @max_data_size: The maximal length of the fw data section + * @max_inst_size: The maximal length of the fw inst section (only DVM) + * @max_data_size: The maximal length of the fw data section (only DVM) * @valid_tx_ant: valid transmit antenna * @valid_rx_ant: valid receive antenna * @non_shared_ant: the antenna that is for WiFi only @@ -314,6 +365,7 @@ struct iwl_pwr_tx_backoff { * @mac_addr_from_csr: read HW address from CSR registers * @features: hw features, any combination of feature_whitelist * @pwr_tx_backoffs: translation table between power limits and backoffs + * @csr: csr flags and addresses that are different across devices * @max_rx_agg_size: max RX aggregation size of the ADDBA request/response * @max_tx_agg_size: max TX aggregation size of the ADDBA request/response * @max_ht_ampdu_factor: the exponent of the max length of A-MPDU that the @@ -333,8 +385,6 @@ struct iwl_pwr_tx_backoff { * @gen2: 22000 and on transport operation * @cdb: CDB support * @nvm_type: see &enum iwl_nvm_type - * @tx_cmd_queue_size: size of the cmd queue. If zero, use the same value as - * the regular queues * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs @@ -354,6 +404,7 @@ struct iwl_cfg { const struct iwl_pwr_tx_backoff *pwr_tx_backoffs; const char *default_nvm_file_C_step; const struct iwl_tt_params *thermal_params; + const struct iwl_csr_params *csr; enum iwl_device_family device_family; enum iwl_led_mode led_mode; enum iwl_nvm_type nvm_type; @@ -369,7 +420,7 @@ struct iwl_cfg { u32 soc_latency; u16 nvm_ver; u16 nvm_calib_ver; - u16 rx_with_siso_diversity:1, + u32 rx_with_siso_diversity:1, bt_shared_single_ant:1, internal_wimax_coex:1, host_interrupt_operation_mode:1, @@ -386,7 +437,6 @@ struct iwl_cfg { gen2:1, cdb:1, dbgc_supported:1; - u16 tx_cmd_queue_size; u8 valid_tx_ant; u8 valid_rx_ant; u8 non_shared_ant; @@ -401,6 +451,36 @@ struct iwl_cfg { u32 extra_phy_cfg_flags; }; +static const struct iwl_csr_params iwl_csr_v1 = { + .flag_mac_clock_ready = 0, + .flag_val_mac_access_en = 0, + .flag_init_done = 2, + .flag_mac_access_req = 3, + .flag_sw_reset = 7, + .flag_master_dis = 8, + .flag_stop_master = 9, + .addr_sw_reset = (CSR_BASE + 0x020), + .mac_addr0_otp = 0x380, + .mac_addr1_otp = 0x384, + .mac_addr0_strap = 0x388, + .mac_addr1_strap = 0x38C +}; + +static const struct iwl_csr_params iwl_csr_v2 = { + .flag_init_done = 6, + .flag_mac_clock_ready = 20, + .flag_val_mac_access_en = 20, + .flag_mac_access_req = 21, + .flag_master_dis = 28, + .flag_stop_master = 29, + .flag_sw_reset = 31, + .addr_sw_reset = (CSR_BASE + 0x024), + .mac_addr0_otp = 0x30, + .mac_addr1_otp = 0x34, + .mac_addr0_strap = 0x38, + .mac_addr1_strap = 0x3C +}; + /* * This list declares the config structures for all devices. */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index 4f0d070eda54..ba971d3946e2 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -8,6 +8,7 @@ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -34,6 +35,7 @@ * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -257,7 +259,6 @@ /* RESET */ #define CSR_RESET_REG_FLAG_NEVO_RESET (0x00000001) #define CSR_RESET_REG_FLAG_FORCE_NMI (0x00000002) -#define CSR_RESET_REG_FLAG_SW_RESET (0x00000080) #define CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100) #define CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200) #define CSR_RESET_LINK_PWR_MGMT_DISABLED (0x80000000) @@ -280,35 +281,10 @@ * 4: GOING_TO_SLEEP * Indicates MAC is entering a power-saving sleep power-down. * Not a good time to access device-internal resources. - * 3: MAC_ACCESS_REQ - * Host sets this to request and maintain MAC wakeup, to allow host - * access to device-internal resources. Host must wait for - * MAC_CLOCK_READY (and !GOING_TO_SLEEP) before accessing non-CSR - * device registers. - * 2: INIT_DONE - * Host sets this to put device into fully operational D0 power mode. - * Host resets this after SW_RESET to put device into low power mode. - * 0: MAC_CLOCK_READY - * Indicates MAC (ucode processor, etc.) is powered up and can run. - * Internal resources are accessible. - * NOTE: This does not indicate that the processor is actually running. - * NOTE: This does not indicate that device has completed - * init or post-power-down restore of internal SRAM memory. - * Use CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that - * SRAM is restored and uCode is in normal operation mode. - * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and - * do not need to save/restore it. - * NOTE: After device reset, this bit remains "0" until host sets - * INIT_DONE */ -#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001) -#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004) -#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008) #define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010) #define CSR_GP_CNTRL_REG_FLAG_XTAL_ON (0x00000400) -#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001) - #define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE (0x07000000) #define CSR_GP_CNTRL_REG_FLAG_RFKILL_WAKE_L1A_EN (0x04000000) #define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW (0x08000000) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index aa2d5c14e202..c59ce4f8a5ed 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -179,6 +179,7 @@ static void iwl_dealloc_ucode(struct iwl_drv *drv) for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) kfree(drv->fw.dbg_trigger_tlv[i]); kfree(drv->fw.dbg_mem_tlv); + kfree(drv->fw.iml); for (i = 0; i < IWL_UCODE_TYPE_MAX; i++) iwl_free_fw_img(drv, drv->fw.img + i); @@ -1126,6 +1127,13 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, pieces->n_dbg_mem_tlv++; break; } + case IWL_UCODE_TLV_IML: { + drv->fw.iml_len = tlv_len; + drv->fw.iml = kmemdup(tlv_data, tlv_len, GFP_KERNEL); + if (!drv->fw.iml) + return -ENOMEM; + break; + } default: IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); break; @@ -1842,3 +1850,9 @@ MODULE_PARM_DESC(d0i3_timeout, "Timeout to D0i3 entry when idle (ms)"); module_param_named(disable_11ac, iwlwifi_mod_params.disable_11ac, bool, 0444); MODULE_PARM_DESC(disable_11ac, "Disable VHT capabilities (default: false)"); + +module_param_named(remove_when_gone, + iwlwifi_mod_params.remove_when_gone, bool, + 0444); +MODULE_PARM_DESC(remove_when_gone, + "Remove dev from PCIe bus if it is deemed inaccessible (default: false)"); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c index 3199d345b427..777f5df8a0c6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c @@ -899,8 +899,8 @@ iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg, EEPROM_SKU_CAP); data->sku_cap_11n_enable = sku & EEPROM_SKU_CAP_11N_ENABLE; data->sku_cap_amt_enable = sku & EEPROM_SKU_CAP_AMT_ENABLE; - data->sku_cap_band_24GHz_enable = sku & EEPROM_SKU_CAP_BAND_24GHZ; - data->sku_cap_band_52GHz_enable = sku & EEPROM_SKU_CAP_BAND_52GHZ; + data->sku_cap_band_24ghz_enable = sku & EEPROM_SKU_CAP_BAND_24GHZ; + data->sku_cap_band_52ghz_enable = sku & EEPROM_SKU_CAP_BAND_52GHZ; data->sku_cap_ipan_enable = sku & EEPROM_SKU_CAP_IPAN_ENABLE; if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) data->sku_cap_11n_enable = false; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h index b33888991b94..8be50ed12300 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h @@ -81,10 +81,11 @@ struct iwl_nvm_data { __le16 kelvin_voltage; __le16 xtal_calib[2]; - bool sku_cap_band_24GHz_enable; - bool sku_cap_band_52GHz_enable; + bool sku_cap_band_24ghz_enable; + bool sku_cap_band_52ghz_enable; bool sku_cap_11n_enable; bool sku_cap_11ac_enable; + bool sku_cap_11ax_enable; bool sku_cap_amt_enable; bool sku_cap_ipan_enable; bool sku_cap_mimo_disabled; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c index f2cea1c7befc..ac965c34a2f8 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -199,12 +201,12 @@ static int iwl_init_otp_access(struct iwl_trans *trans) /* Enable 40MHz radio clock */ iwl_write32(trans, CSR_GP_CNTRL, iwl_read32(trans, CSR_GP_CNTRL) | - CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + BIT(trans->cfg->csr->flag_init_done)); /* wait for clock to be ready */ ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + BIT(trans->cfg->csr->flag_mac_clock_ready), + BIT(trans->cfg->csr->flag_mac_clock_ready), 25000); if (ret < 0) { IWL_ERR(trans, "Time out access OTP\n"); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h index a41c46e63eb1..a7dd8a8cddf9 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h @@ -122,6 +122,7 @@ enum iwl_uapsd_disable { * @lar_disable: disable LAR (regulatory), default = 0 * @fw_monitor: allow to use firmware monitor * @disable_11ac: disable VHT capabilities, default = false. + * @remove_when_gone: remove an inaccessible device from the PCIe bus. */ struct iwl_mod_params { int swcrypto; @@ -143,6 +144,7 @@ struct iwl_mod_params { bool lar_disable; bool fw_monitor; bool disable_11ac; + bool remove_when_gone; }; #endif /* #__iwl_modparams_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index ca0174680af9..b815ba38dbdb 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -8,6 +8,7 @@ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -35,6 +36,7 @@ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -68,6 +70,7 @@ #include <linux/export.h> #include <linux/etherdevice.h> #include <linux/pci.h> +#include <linux/firmware.h> #include "iwl-drv.h" #include "iwl-modparams.h" @@ -77,6 +80,9 @@ #include "iwl-csr.h" #include "fw/acpi.h" #include "fw/api/nvm-reg.h" +#include "fw/api/commands.h" +#include "fw/api/cmdhdr.h" +#include "fw/img.h" /* NVM offsets (in words) definitions */ enum nvm_offsets { @@ -292,7 +298,7 @@ static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, bool is_5ghz, static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, struct iwl_nvm_data *data, const __le16 * const nvm_ch_flags, - bool lar_supported, bool no_wide_in_5ghz) + u32 sbands_flags) { int ch_idx; int n_channels = 0; @@ -316,11 +322,12 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx); - if (is_5ghz && !data->sku_cap_band_52GHz_enable) + if (is_5ghz && !data->sku_cap_band_52ghz_enable) continue; /* workaround to disable wide channels in 5GHz */ - if (no_wide_in_5ghz && is_5ghz) { + if ((sbands_flags & IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ) && + is_5ghz) { ch_flags &= ~(NVM_CHANNEL_40MHZ | NVM_CHANNEL_80MHZ | NVM_CHANNEL_160MHZ); @@ -329,7 +336,8 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, if (ch_flags & NVM_CHANNEL_160MHZ) data->vht160_supported = true; - if (!lar_supported && !(ch_flags & NVM_CHANNEL_VALID)) { + if (!(sbands_flags & IWL_NVM_SBANDS_FLAGS_LAR) && + !(ch_flags & NVM_CHANNEL_VALID)) { /* * Channels might become valid later if lar is * supported, hence we still want to add them to @@ -359,7 +367,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, channel->max_power = IWL_DEFAULT_MAX_TX_POWER; /* don't put limitations in case we're using LAR */ - if (!lar_supported) + if (!(sbands_flags & IWL_NVM_SBANDS_FLAGS_LAR)) channel->flags = iwl_get_channel_flags(nvm_chan[ch_idx], ch_idx, is_5ghz, ch_flags, cfg); @@ -455,17 +463,17 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map; } -void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, const __le16 *nvm_ch_flags, - u8 tx_chains, u8 rx_chains, bool lar_supported, - bool no_wide_in_5ghz) +static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, + const __le16 *nvm_ch_flags, u8 tx_chains, + u8 rx_chains, u32 sbands_flags) { int n_channels; int n_used = 0; struct ieee80211_supported_band *sband; n_channels = iwl_init_channel_map(dev, cfg, data, nvm_ch_flags, - lar_supported, no_wide_in_5ghz); + sbands_flags); sband = &data->bands[NL80211_BAND_2GHZ]; sband->band = NL80211_BAND_2GHZ; sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; @@ -491,7 +499,6 @@ void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n", n_used, n_channels); } -IWL_EXPORT_SYMBOL(iwl_init_sbands); static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw, const __le16 *phy_sku) @@ -569,11 +576,15 @@ static void iwl_flip_hw_address(__le32 mac_addr0, __le32 mac_addr1, u8 *dest) dest[5] = hw_addr[0]; } -void iwl_set_hw_address_from_csr(struct iwl_trans *trans, - struct iwl_nvm_data *data) +static void iwl_set_hw_address_from_csr(struct iwl_trans *trans, + struct iwl_nvm_data *data) { - __le32 mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_STRAP)); - __le32 mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_STRAP)); + __le32 mac_addr0 = + cpu_to_le32(iwl_read32(trans, + trans->cfg->csr->mac_addr0_strap)); + __le32 mac_addr1 = + cpu_to_le32(iwl_read32(trans, + trans->cfg->csr->mac_addr1_strap)); iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr); /* @@ -583,12 +594,13 @@ void iwl_set_hw_address_from_csr(struct iwl_trans *trans, if (is_valid_ether_addr(data->hw_addr)) return; - mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_OTP)); - mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_OTP)); + mac_addr0 = cpu_to_le32(iwl_read32(trans, + trans->cfg->csr->mac_addr0_otp)); + mac_addr1 = cpu_to_le32(iwl_read32(trans, + trans->cfg->csr->mac_addr1_otp)); iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr); } -IWL_EXPORT_SYMBOL(iwl_set_hw_address_from_csr); static void iwl_set_hw_address_family_8000(struct iwl_trans *trans, const struct iwl_cfg *cfg, @@ -713,8 +725,8 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, struct device *dev = trans->dev; struct iwl_nvm_data *data; bool lar_enabled; - bool no_wide_in_5ghz = iwl_nvm_no_wide_in_5ghz(dev, cfg, nvm_hw); u32 sku, radio_cfg; + u32 sbands_flags = 0; u16 lar_config; const __le16 *ch_section; @@ -741,8 +753,8 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, rx_chains &= data->valid_rx_ant; sku = iwl_get_sku(cfg, nvm_sw, phy_sku); - data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ; - data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ; + data->sku_cap_band_24ghz_enable = sku & NVM_SKU_CAP_BAND_24GHZ; + data->sku_cap_band_52ghz_enable = sku & NVM_SKU_CAP_BAND_52GHZ; data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE; if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) data->sku_cap_11n_enable = false; @@ -787,8 +799,14 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, return NULL; } + if (lar_fw_supported && lar_enabled) + sbands_flags |= IWL_NVM_SBANDS_FLAGS_LAR; + + if (iwl_nvm_no_wide_in_5ghz(dev, cfg, nvm_hw)) + sbands_flags |= IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ; + iwl_init_sbands(dev, cfg, data, ch_section, tx_chains, rx_chains, - lar_fw_supported && lar_enabled, no_wide_in_5ghz); + sbands_flags); data->calib_version = 255; return data; @@ -1007,8 +1025,7 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, continue; copy_rd->reg_rules[i].wmm_rule = d_wmm + - (regd->reg_rules[i].wmm_rule - s_wmm) / - sizeof(struct ieee80211_wmm_rule); + (regd->reg_rules[i].wmm_rule - s_wmm); } out: @@ -1017,3 +1034,294 @@ out: return copy_rd; } IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info); + +#define IWL_MAX_NVM_SECTION_SIZE 0x1b58 +#define IWL_MAX_EXT_NVM_SECTION_SIZE 0x1ffc +#define MAX_NVM_FILE_LEN 16384 + +void iwl_nvm_fixups(u32 hw_id, unsigned int section, u8 *data, + unsigned int len) +{ +#define IWL_4165_DEVICE_ID 0x5501 +#define NVM_SKU_CAP_MIMO_DISABLE BIT(5) + + if (section == NVM_SECTION_TYPE_PHY_SKU && + hw_id == IWL_4165_DEVICE_ID && data && len >= 5 && + (data[4] & NVM_SKU_CAP_MIMO_DISABLE)) + /* OTP 0x52 bug work around: it's a 1x1 device */ + data[3] = ANT_B | (ANT_B << 4); +} +IWL_EXPORT_SYMBOL(iwl_nvm_fixups); + +/* + * Reads external NVM from a file into mvm->nvm_sections + * + * HOW TO CREATE THE NVM FILE FORMAT: + * ------------------------------ + * 1. create hex file, format: + * 3800 -> header + * 0000 -> header + * 5a40 -> data + * + * rev - 6 bit (word1) + * len - 10 bit (word1) + * id - 4 bit (word2) + * rsv - 12 bit (word2) + * + * 2. flip 8bits with 8 bits per line to get the right NVM file format + * + * 3. create binary file from the hex file + * + * 4. save as "iNVM_xxx.bin" under /lib/firmware + */ +int iwl_read_external_nvm(struct iwl_trans *trans, + const char *nvm_file_name, + struct iwl_nvm_section *nvm_sections) +{ + int ret, section_size; + u16 section_id; + const struct firmware *fw_entry; + const struct { + __le16 word1; + __le16 word2; + u8 data[]; + } *file_sec; + const u8 *eof; + u8 *temp; + int max_section_size; + const __le32 *dword_buff; + +#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF)) +#define NVM_WORD2_ID(x) (x >> 12) +#define EXT_NVM_WORD2_LEN(x) (2 * (((x) & 0xFF) << 8 | (x) >> 8)) +#define EXT_NVM_WORD1_ID(x) ((x) >> 4) +#define NVM_HEADER_0 (0x2A504C54) +#define NVM_HEADER_1 (0x4E564D2A) +#define NVM_HEADER_SIZE (4 * sizeof(u32)) + + IWL_DEBUG_EEPROM(trans->dev, "Read from external NVM\n"); + + /* Maximal size depends on NVM version */ + if (trans->cfg->nvm_type != IWL_NVM_EXT) + max_section_size = IWL_MAX_NVM_SECTION_SIZE; + else + max_section_size = IWL_MAX_EXT_NVM_SECTION_SIZE; + + /* + * Obtain NVM image via request_firmware. Since we already used + * request_firmware_nowait() for the firmware binary load and only + * get here after that we assume the NVM request can be satisfied + * synchronously. + */ + ret = request_firmware(&fw_entry, nvm_file_name, trans->dev); + if (ret) { + IWL_ERR(trans, "ERROR: %s isn't available %d\n", + nvm_file_name, ret); + return ret; + } + + IWL_INFO(trans, "Loaded NVM file %s (%zu bytes)\n", + nvm_file_name, fw_entry->size); + + if (fw_entry->size > MAX_NVM_FILE_LEN) { + IWL_ERR(trans, "NVM file too large\n"); + ret = -EINVAL; + goto out; + } + + eof = fw_entry->data + fw_entry->size; + dword_buff = (__le32 *)fw_entry->data; + + /* some NVM file will contain a header. + * The header is identified by 2 dwords header as follow: + * dword[0] = 0x2A504C54 + * dword[1] = 0x4E564D2A + * + * This header must be skipped when providing the NVM data to the FW. + */ + if (fw_entry->size > NVM_HEADER_SIZE && + dword_buff[0] == cpu_to_le32(NVM_HEADER_0) && + dword_buff[1] == cpu_to_le32(NVM_HEADER_1)) { + file_sec = (void *)(fw_entry->data + NVM_HEADER_SIZE); + IWL_INFO(trans, "NVM Version %08X\n", le32_to_cpu(dword_buff[2])); + IWL_INFO(trans, "NVM Manufacturing date %08X\n", + le32_to_cpu(dword_buff[3])); + + /* nvm file validation, dword_buff[2] holds the file version */ + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 && + CSR_HW_REV_STEP(trans->hw_rev) == SILICON_C_STEP && + le32_to_cpu(dword_buff[2]) < 0xE4A) { + ret = -EFAULT; + goto out; + } + } else { + file_sec = (void *)fw_entry->data; + } + + while (true) { + if (file_sec->data > eof) { + IWL_ERR(trans, + "ERROR - NVM file too short for section header\n"); + ret = -EINVAL; + break; + } + + /* check for EOF marker */ + if (!file_sec->word1 && !file_sec->word2) { + ret = 0; + break; + } + + if (trans->cfg->nvm_type != IWL_NVM_EXT) { + section_size = + 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1)); + section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2)); + } else { + section_size = 2 * EXT_NVM_WORD2_LEN( + le16_to_cpu(file_sec->word2)); + section_id = EXT_NVM_WORD1_ID( + le16_to_cpu(file_sec->word1)); + } + + if (section_size > max_section_size) { + IWL_ERR(trans, "ERROR - section too large (%d)\n", + section_size); + ret = -EINVAL; + break; + } + + if (!section_size) { + IWL_ERR(trans, "ERROR - section empty\n"); + ret = -EINVAL; + break; + } + + if (file_sec->data + section_size > eof) { + IWL_ERR(trans, + "ERROR - NVM file too short for section (%d bytes)\n", + section_size); + ret = -EINVAL; + break; + } + + if (WARN(section_id >= NVM_MAX_NUM_SECTIONS, + "Invalid NVM section ID %d\n", section_id)) { + ret = -EINVAL; + break; + } + + temp = kmemdup(file_sec->data, section_size, GFP_KERNEL); + if (!temp) { + ret = -ENOMEM; + break; + } + + iwl_nvm_fixups(trans->hw_id, section_id, temp, section_size); + + kfree(nvm_sections[section_id].data); + nvm_sections[section_id].data = temp; + nvm_sections[section_id].length = section_size; + + /* advance to the next section */ + file_sec = (void *)(file_sec->data + section_size); + } +out: + release_firmware(fw_entry); + return ret; +} +IWL_EXPORT_SYMBOL(iwl_read_external_nvm); + +struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, + const struct iwl_fw *fw) +{ + struct iwl_nvm_get_info cmd = {}; + struct iwl_nvm_get_info_rsp *rsp; + struct iwl_nvm_data *nvm; + struct iwl_host_cmd hcmd = { + .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, + .data = { &cmd, }, + .len = { sizeof(cmd) }, + .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, NVM_GET_INFO) + }; + int ret; + bool lar_fw_supported = !iwlwifi_mod_params.lar_disable && + fw_has_capa(&fw->ucode_capa, + IWL_UCODE_TLV_CAPA_LAR_SUPPORT); + u32 mac_flags; + u32 sbands_flags = 0; + + ret = iwl_trans_send_cmd(trans, &hcmd); + if (ret) + return ERR_PTR(ret); + + if (WARN(iwl_rx_packet_payload_len(hcmd.resp_pkt) != sizeof(*rsp), + "Invalid payload len in NVM response from FW %d", + iwl_rx_packet_payload_len(hcmd.resp_pkt))) { + ret = -EINVAL; + goto out; + } + + rsp = (void *)hcmd.resp_pkt->data; + if (le32_to_cpu(rsp->general.flags) & NVM_GENERAL_FLAGS_EMPTY_OTP) + IWL_INFO(trans, "OTP is empty\n"); + + nvm = kzalloc(sizeof(*nvm) + + sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS, + GFP_KERNEL); + if (!nvm) { + ret = -ENOMEM; + goto out; + } + + iwl_set_hw_address_from_csr(trans, nvm); + /* TODO: if platform NVM has MAC address - override it here */ + + if (!is_valid_ether_addr(nvm->hw_addr)) { + IWL_ERR(trans, "no valid mac address was found\n"); + ret = -EINVAL; + goto err_free; + } + + IWL_INFO(trans, "base HW address: %pM\n", nvm->hw_addr); + + /* Initialize general data */ + nvm->nvm_version = le16_to_cpu(rsp->general.nvm_version); + + /* Initialize MAC sku data */ + mac_flags = le32_to_cpu(rsp->mac_sku.mac_sku_flags); + nvm->sku_cap_11ac_enable = + !!(mac_flags & NVM_MAC_SKU_FLAGS_802_11AC_ENABLED); + nvm->sku_cap_11n_enable = + !!(mac_flags & NVM_MAC_SKU_FLAGS_802_11N_ENABLED); + nvm->sku_cap_band_24ghz_enable = + !!(mac_flags & NVM_MAC_SKU_FLAGS_BAND_2_4_ENABLED); + nvm->sku_cap_band_52ghz_enable = + !!(mac_flags & NVM_MAC_SKU_FLAGS_BAND_5_2_ENABLED); + nvm->sku_cap_mimo_disabled = + !!(mac_flags & NVM_MAC_SKU_FLAGS_MIMO_DISABLED); + + /* Initialize PHY sku data */ + nvm->valid_tx_ant = (u8)le32_to_cpu(rsp->phy_sku.tx_chains); + nvm->valid_rx_ant = (u8)le32_to_cpu(rsp->phy_sku.rx_chains); + + if (le32_to_cpu(rsp->regulatory.lar_enabled) && lar_fw_supported) { + nvm->lar_enabled = true; + sbands_flags |= IWL_NVM_SBANDS_FLAGS_LAR; + } + + iwl_init_sbands(trans->dev, trans->cfg, nvm, + rsp->regulatory.channel_profile, + nvm->valid_tx_ant & fw->valid_tx_ant, + nvm->valid_rx_ant & fw->valid_rx_ant, + sbands_flags); + + iwl_free_resp(&hcmd); + return nvm; + +err_free: + kfree(nvm); +out: + iwl_free_resp(&hcmd); + return ERR_PTR(ret); +} +IWL_EXPORT_SYMBOL(iwl_get_nvm); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h index 3071a23b7606..234d1009a9de 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -7,6 +7,7 @@ * * Copyright(c) 2008 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -68,6 +70,17 @@ #include "iwl-eeprom-parse.h" /** + * enum iwl_nvm_sbands_flags - modification flags for the channel profiles + * + * @IWL_NVM_SBANDS_FLAGS_LAR: LAR is enabled + * @IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ: disallow 40, 80 and 160MHz on 5GHz + */ +enum iwl_nvm_sbands_flags { + IWL_NVM_SBANDS_FLAGS_LAR = BIT(0), + IWL_NVM_SBANDS_FLAGS_NO_WIDE_IN_5GHZ = BIT(1), +}; + +/** * iwl_parse_nvm_data - parse NVM data and return values * * This function parses all NVM values we need and then @@ -83,20 +96,6 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, u8 tx_chains, u8 rx_chains, bool lar_fw_supported); /** - * iwl_set_hw_address_from_csr - sets HW address for 9000 devices and on - */ -void iwl_set_hw_address_from_csr(struct iwl_trans *trans, - struct iwl_nvm_data *data); - -/** - * iwl_init_sbands - parse and set all channel profiles - */ -void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, const __le16 *nvm_ch_flags, - u8 tx_chains, u8 rx_chains, bool lar_supported, - bool no_wide_in_5ghz); - -/** * iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW * * This function parses the regulatory channel data received as a @@ -111,4 +110,33 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, int num_of_ch, __le32 *channels, u16 fw_mcc, u16 geo_info); +/** + * struct iwl_nvm_section - describes an NVM section in memory. + * + * This struct holds an NVM section read from the NIC using NVM_ACCESS_CMD, + * and saved for later use by the driver. Not all NVM sections are saved + * this way, only the needed ones. + */ +struct iwl_nvm_section { + u16 length; + const u8 *data; +}; + +/** + * iwl_read_external_nvm - Reads external NVM from a file into nvm_sections + */ +int iwl_read_external_nvm(struct iwl_trans *trans, + const char *nvm_file_name, + struct iwl_nvm_section *nvm_sections); +void iwl_nvm_fixups(u32 hw_id, unsigned int section, u8 *data, + unsigned int len); + +/** + * iwl_get_nvm - retrieve NVM data from firmware + * + * Allocates a new iwl_nvm_data structure, fills it with + * NVM data, and returns it to caller. + */ +struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, + const struct iwl_fw *fw); #endif /* __iwl_nvm_parse_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index c25ed1a0bbb0..1b9c627ee34d 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -554,7 +554,7 @@ struct iwl_trans_ops { /* 22000 functions */ int (*txq_alloc)(struct iwl_trans *trans, struct iwl_tx_queue_cfg_cmd *cmd, - int cmd_id, + int cmd_id, int size, unsigned int queue_wdg_timeout); void (*txq_free)(struct iwl_trans *trans, int queue); @@ -691,6 +691,8 @@ enum iwl_plat_pm_mode { * @wide_cmd_header: true when ucode supports wide command header format * @num_rx_queues: number of RX queues allocated by the transport; * the transport must set this before calling iwl_drv_start() + * @iml_len: the length of the image loader + * @iml: a pointer to the image loader itself * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only. * The user should use iwl_trans_{alloc,free}_tx_cmd. * @rx_mpdu_cmd: MPDU RX command ID, must be assigned by opmode before @@ -735,6 +737,9 @@ struct iwl_trans { u8 num_rx_queues; + size_t iml_len; + u8 *iml; + /* The following fields are internal only */ struct kmem_cache *dev_cmd_pool; char dev_cmd_pool_name[50]; @@ -952,8 +957,8 @@ iwl_trans_txq_free(struct iwl_trans *trans, int queue) static inline int iwl_trans_txq_alloc(struct iwl_trans *trans, struct iwl_tx_queue_cfg_cmd *cmd, - int cmd_id, - unsigned int queue_wdg_timeout) + int cmd_id, int size, + unsigned int wdg_timeout) { might_sleep(); @@ -965,7 +970,7 @@ iwl_trans_txq_alloc(struct iwl_trans *trans, return -EIO; } - return trans->ops->txq_alloc(trans, cmd, cmd_id, queue_wdg_timeout); + return trans->ops->txq_alloc(trans, cmd, cmd_id, size, wdg_timeout); } static inline void iwl_trans_txq_set_shared_mode(struct iwl_trans *trans, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index 890dbfff3a06..016e03a5034f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -279,6 +279,8 @@ struct iwl_bt_iterator_data { struct ieee80211_chanctx_conf *primary; struct ieee80211_chanctx_conf *secondary; bool primary_ll; + u8 primary_load; + u8 secondary_load; }; static inline @@ -295,6 +297,30 @@ void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm, enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0; } +#define MVM_COEX_TCM_PERIOD (HZ * 10) + +static void iwl_mvm_bt_coex_tcm_based_ci(struct iwl_mvm *mvm, + struct iwl_bt_iterator_data *data) +{ + unsigned long now = jiffies; + + if (!time_after(now, mvm->bt_coex_last_tcm_ts + MVM_COEX_TCM_PERIOD)) + return; + + mvm->bt_coex_last_tcm_ts = now; + + /* We assume here that we don't have more than 2 vifs on 2.4GHz */ + + /* if the primary is low latency, it will stay primary */ + if (data->primary_ll) + return; + + if (data->primary_load >= data->secondary_load) + return; + + swap(data->primary, data->secondary); +} + /* must be called under rcu_read_lock */ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) @@ -385,6 +411,11 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, /* there is low latency vif - we will be secondary */ data->secondary = chanctx_conf; } + + if (data->primary == chanctx_conf) + data->primary_load = mvm->tcm.result.load[mvmvif->id]; + else if (data->secondary == chanctx_conf) + data->secondary_load = mvm->tcm.result.load[mvmvif->id]; return; } @@ -398,6 +429,10 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, /* if secondary is not NULL, it might be a GO */ data->secondary = chanctx_conf; + if (data->primary == chanctx_conf) + data->primary_load = mvm->tcm.result.load[mvmvif->id]; + else if (data->secondary == chanctx_conf) + data->secondary_load = mvm->tcm.result.load[mvmvif->id]; /* * don't reduce the Tx power if one of these is true: * we are in LOOSE @@ -449,6 +484,8 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_bt_notif_iterator, &data); + iwl_mvm_bt_coex_tcm_based_ci(mvm, &data); + if (data.primary) { struct ieee80211_chanctx_conf *chan = data.primary; if (WARN_ON(!chan->def.chan)) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h index 96b52a275ee3..d61ff66ce07b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h @@ -69,6 +69,8 @@ #include <linux/ieee80211.h> +#define IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM 20 + #define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT (100 * USEC_PER_MSEC) #define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT (100 * USEC_PER_MSEC) #define IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT (10 * USEC_PER_MSEC) @@ -112,6 +114,11 @@ #define IWL_MVM_PARSE_NVM 0 #define IWL_MVM_ADWELL_ENABLE 1 #define IWL_MVM_ADWELL_MAX_BUDGET 0 +#define IWL_MVM_TCM_LOAD_MEDIUM_THRESH 10 /* percentage */ +#define IWL_MVM_TCM_LOAD_HIGH_THRESH 50 /* percentage */ +#define IWL_MVM_TCM_LOWLAT_ENABLE_THRESH 100 /* packets/10 seconds */ +#define IWL_MVM_UAPSD_NONAGG_PERIOD 5000 /* msecs */ +#define IWL_MVM_UAPSD_NOAGG_LIST_LEN IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM #define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE 1 #define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE 2 #define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE_TW 1 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 2efe9b099556..3fcf489f3120 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -18,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -35,6 +31,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -693,6 +690,14 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm, IWL_WOWLAN_WAKEUP_LINK_CHANGE); } + if (wowlan->any) { + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS | + IWL_WOWLAN_WAKEUP_LINK_CHANGE | + IWL_WOWLAN_WAKEUP_RX_FRAME | + IWL_WOWLAN_WAKEUP_BCN_FILTERING); + } + return 0; } @@ -1097,6 +1102,7 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* make sure the d0i3 exit work is not pending */ flush_work(&mvm->d0i3_exit_work); + iwl_mvm_pause_tcm(mvm, true); iwl_fw_runtime_suspend(&mvm->fwrt); @@ -2014,6 +2020,8 @@ int iwl_mvm_resume(struct ieee80211_hw *hw) mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; + iwl_mvm_resume_tcm(mvm); + iwl_fw_runtime_resume(&mvm->fwrt); return ret; @@ -2042,6 +2050,8 @@ static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file) mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3; + iwl_mvm_pause_tcm(mvm, true); + iwl_fw_runtime_suspend(&mvm->fwrt); /* start pseudo D3 */ @@ -2104,6 +2114,8 @@ static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file) __iwl_mvm_resume(mvm, true); rtnl_unlock(); + iwl_mvm_resume_tcm(mvm); + iwl_fw_runtime_resume(&mvm->fwrt); mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c index f7fcf700196b..798605c4f122 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c @@ -269,6 +269,8 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file, mvmvif->id, mvmvif->color); pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n", vif->bss_conf.bssid); + pos += scnprintf(buf+pos, bufsz-pos, "Load: %d\n", + mvm->tcm.result.load[mvmvif->id]); pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n"); for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++) pos += scnprintf(buf+pos, bufsz-pos, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 0e6401cd7ccc..1c4178f20441 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -1728,6 +1728,27 @@ iwl_dbgfs_send_echo_cmd_write(struct iwl_mvm *mvm, char *buf, return ret ?: count; } +static ssize_t +iwl_dbgfs_uapsd_noagg_bssids_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + u8 buf[IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM * ETH_ALEN * 3 + 1]; + unsigned int pos = 0; + size_t bufsz = sizeof(buf); + int i; + + mutex_lock(&mvm->mutex); + + for (i = 0; i < IWL_MVM_UAPSD_NOAGG_LIST_LEN; i++) + pos += scnprintf(buf + pos, bufsz - pos, "%pM\n", + mvm->uapsd_noagg_bssids[i].addr); + + mutex_unlock(&mvm->mutex); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64); /* Device wide debugfs entries */ @@ -1762,6 +1783,8 @@ MVM_DEBUGFS_WRITE_FILE_OPS(indirection_tbl, (IWL_RSS_INDIRECTION_TABLE_SIZE * 2)); MVM_DEBUGFS_WRITE_FILE_OPS(inject_packet, 512); +MVM_DEBUGFS_READ_FILE_OPS(uapsd_noagg_bssids); + #ifdef CONFIG_IWLWIFI_BCAST_FILTERING MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256); MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256); @@ -1972,6 +1995,8 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) mvm->debugfs_dir, &mvm->drop_bcn_ap_mode)) goto err; + MVM_DEBUGFS_ADD_FILE(uapsd_noagg_bssids, mvm->debugfs_dir, S_IRUSR); + #ifdef CONFIG_IWLWIFI_BCAST_FILTERING if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) { bcast_dir = debugfs_create_dir("bcast_filtering", diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 3c59109bea20..866c91c923be 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -35,6 +36,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -79,6 +81,8 @@ #include "mvm.h" #include "fw/dbg.h" #include "iwl-phy-db.h" +#include "iwl-modparams.h" +#include "iwl-nvm-parse.h" #define MVM_UCODE_ALIVE_TIMEOUT HZ #define MVM_UCODE_CALIB_TIMEOUT (2*HZ) @@ -381,7 +385,8 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) /* Load NVM to NIC if needed */ if (mvm->nvm_file_name) { - iwl_mvm_read_external_nvm(mvm); + iwl_read_external_nvm(mvm->trans, mvm->nvm_file_name, + mvm->nvm_sections); iwl_mvm_load_nvm_to_nic(mvm); } @@ -410,7 +415,7 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) /* Read the NVM only at driver load time, no need to do this twice */ if (!IWL_MVM_PARSE_NVM && read_nvm) { - mvm->nvm_data = iwl_fw_get_nvm(&mvm->fwrt); + mvm->nvm_data = iwl_get_nvm(mvm->trans, mvm->fw); if (IS_ERR(mvm->nvm_data)) { ret = PTR_ERR(mvm->nvm_data); mvm->nvm_data = NULL; @@ -1093,6 +1098,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { mvm->scan_type = IWL_SCAN_TYPE_NOT_SET; + mvm->hb_scan_type = IWL_SCAN_TYPE_NOT_SET; ret = iwl_mvm_config_scan(mvm); if (ret) goto error; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 9b2e1cb58e38..a6e072234398 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -953,6 +953,16 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, switch (action) { case IEEE80211_AMPDU_RX_START: + if (iwl_mvm_vif_from_mac80211(vif)->ap_sta_id == + iwl_mvm_sta_from_mac80211(sta)->sta_id) { + struct iwl_mvm_vif *mvmvif; + u16 macid = iwl_mvm_vif_from_mac80211(vif)->id; + struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[macid]; + + mdata->opened_rx_ba_sessions = true; + mvmvif = iwl_mvm_vif_from_mac80211(vif); + cancel_delayed_work(&mvmvif->uapsd_nonagg_detected_wk); + } if (!iwl_enable_rx_ampdu(mvm->cfg)) { ret = -EINVAL; break; @@ -1436,6 +1446,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, mvm->p2p_device_vif = vif; } + iwl_mvm_tcm_add_vif(mvm, vif); + if (vif->type == NL80211_IFTYPE_MONITOR) mvm->monitor_on = true; @@ -1487,6 +1499,10 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, iwl_mvm_prepare_mac_removal(mvm, vif); + if (!(vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC)) + iwl_mvm_tcm_rm_vif(mvm, vif); + mutex_lock(&mvm->mutex); if (mvm->bf_allowed_vif == mvmvif) { @@ -2536,6 +2552,16 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, const u8 *bssid) { + int i; + + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + struct iwl_mvm_tcm_mac *mdata; + + mdata = &mvm->tcm.data[iwl_mvm_vif_from_mac80211(vif)->id]; + ewma_rate_init(&mdata->uapsd_nonagg_detect.rate); + mdata->opened_rx_ba_sessions = false; + } + if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT)) return; @@ -2550,6 +2576,13 @@ static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return; } + for (i = 0; i < IWL_MVM_UAPSD_NOAGG_LIST_LEN; i++) { + if (ether_addr_equal(mvm->uapsd_noagg_bssids[i].addr, bssid)) { + vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD; + return; + } + } + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; } @@ -2652,7 +2685,7 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); /* track whether or not the station is associated */ - mvm_sta->associated = new_state >= IEEE80211_STA_ASSOC; + mvm_sta->sta_state = new_state; if (old_state == IEEE80211_STA_NOTEXIST && new_state == IEEE80211_STA_NONE) { @@ -2704,8 +2737,7 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); } - iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band, - true); + iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band); ret = iwl_mvm_update_sta(mvm, vif, sta); } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { @@ -2721,8 +2753,7 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, /* enable beacon filtering */ WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); - iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band, - false); + iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band); ret = 0; } else if (old_state == IEEE80211_STA_AUTHORIZED && @@ -2811,7 +2842,8 @@ static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw, } static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) + struct ieee80211_vif *vif, + u16 req_duration) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); u32 duration = IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS; @@ -2824,6 +2856,9 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PREPARE_TX)) return; + if (req_duration > duration) + duration = req_duration; + mutex_lock(&mvm->mutex); /* Try really hard to protect the session and hear a beacon */ iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500, false); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index d2cf751db68d..6a4ba160c59e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -35,6 +36,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -90,7 +92,9 @@ #include "fw/runtime.h" #include "fw/dbg.h" #include "fw/acpi.h" -#include "fw/debugfs.h" +#include "iwl-nvm-parse.h" + +#include <linux/average.h> #define IWL_MVM_MAX_ADDRESSES 5 /* RSSI offset for WkP */ @@ -444,6 +448,8 @@ struct iwl_mvm_vif { /* FW identified misbehaving AP */ u8 uapsd_misbehaving_bssid[ETH_ALEN]; + struct delayed_work uapsd_nonagg_detected_wk; + /* Indicates that CSA countdown may be started */ bool csa_countdown; bool csa_failed; @@ -503,18 +509,6 @@ enum iwl_mvm_sched_scan_pass_all_states { }; /** - * struct iwl_nvm_section - describes an NVM section in memory. - * - * This struct holds an NVM section read from the NIC using NVM_ACCESS_CMD, - * and saved for later use by the driver. Not all NVM sections are saved - * this way, only the needed ones. - */ -struct iwl_nvm_section { - u16 length; - const u8 *data; -}; - -/** * struct iwl_mvm_tt_mgnt - Thermal Throttling Management structure * @ct_kill_exit: worker to exit thermal kill * @dynamic_smps: Is thermal throttling enabled dynamic_smps? @@ -595,6 +589,53 @@ enum iwl_mvm_tdls_cs_state { IWL_MVM_TDLS_SW_ACTIVE, }; +enum iwl_mvm_traffic_load { + IWL_MVM_TRAFFIC_LOW, + IWL_MVM_TRAFFIC_MEDIUM, + IWL_MVM_TRAFFIC_HIGH, +}; + +DECLARE_EWMA(rate, 16, 16) + +struct iwl_mvm_tcm_mac { + struct { + u32 pkts[IEEE80211_NUM_ACS]; + u32 airtime; + } tx; + struct { + u32 pkts[IEEE80211_NUM_ACS]; + u32 airtime; + u32 last_ampdu_ref; + } rx; + struct { + /* track AP's transfer in client mode */ + u64 rx_bytes; + struct ewma_rate rate; + bool detected; + } uapsd_nonagg_detect; + bool opened_rx_ba_sessions; +}; + +struct iwl_mvm_tcm { + struct delayed_work work; + spinlock_t lock; /* used when time elapsed */ + unsigned long ts; /* timestamp when period ends */ + unsigned long ll_ts; + unsigned long uapsd_nonagg_ts; + bool paused; + struct iwl_mvm_tcm_mac data[NUM_MAC_INDEX_DRIVER]; + struct { + u32 elapsed; /* milliseconds for this TCM period */ + u32 airtime[NUM_MAC_INDEX_DRIVER]; + enum iwl_mvm_traffic_load load[NUM_MAC_INDEX_DRIVER]; + enum iwl_mvm_traffic_load band_load[NUM_NL80211_BANDS]; + enum iwl_mvm_traffic_load global_load; + bool low_latency[NUM_MAC_INDEX_DRIVER]; + bool change[NUM_MAC_INDEX_DRIVER]; + bool global_change; + } result; +}; + /** * struct iwl_mvm_reorder_buffer - per ra/tid/queue reorder buffer * @head_sn: reorder window head sn @@ -829,7 +870,10 @@ struct iwl_mvm { unsigned int scan_status; void *scan_cmd; struct iwl_mcast_filter_cmd *mcast_filter_cmd; + /* For CDB this is low band scan type, for non-CDB - type. */ enum iwl_mvm_scan_type scan_type; + enum iwl_mvm_scan_type hb_scan_type; + enum iwl_mvm_sched_scan_pass_all_states sched_scan_pass_all; struct delayed_work scan_timeout_dwork; @@ -978,6 +1022,13 @@ struct iwl_mvm { */ bool temperature_test; /* Debug test temperature is enabled */ + unsigned long bt_coex_last_tcm_ts; + struct iwl_mvm_tcm tcm; + + u8 uapsd_noagg_bssid_write_idx; + struct mac_address uapsd_noagg_bssids[IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM] + __aligned(2); + struct iwl_time_quota_cmd last_quota_cmd; #ifdef CONFIG_NL80211_TESTMODE @@ -1293,6 +1344,16 @@ static inline bool iwl_mvm_is_cdb_supported(struct iwl_mvm *mvm) IWL_UCODE_TLV_CAPA_CDB_SUPPORT); } +static inline bool iwl_mvm_cdb_scan_api(struct iwl_mvm *mvm) +{ + /* + * TODO: should this be the same as iwl_mvm_is_cdb_supported()? + * but then there's a little bit of code in scan that won't make + * any sense... + */ + return mvm->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22000; +} + static inline bool iwl_mvm_has_new_rx_stats_api(struct iwl_mvm *mvm) { return fw_has_api(&mvm->fw->ucode_capa, @@ -1438,7 +1499,6 @@ void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm); /* NVM */ int iwl_nvm_init(struct iwl_mvm *mvm); int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm); -int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm); static inline u8 iwl_mvm_get_valid_tx_ant(struct iwl_mvm *mvm) { @@ -1771,6 +1831,8 @@ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum iwl_mvm_low_latency_cause cause); /* get SystemLowLatencyMode - only needed for beacon threshold? */ bool iwl_mvm_low_latency(struct iwl_mvm *mvm); +bool iwl_mvm_low_latency_band(struct iwl_mvm *mvm, enum nl80211_band band); + /* get VMACLowLatencyMode */ static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif) { @@ -1906,6 +1968,17 @@ bool iwl_mvm_is_vif_assoc(struct iwl_mvm *mvm); void iwl_mvm_inactivity_check(struct iwl_mvm *mvm); +#define MVM_TCM_PERIOD_MSEC 500 +#define MVM_TCM_PERIOD (HZ * MVM_TCM_PERIOD_MSEC / 1000) +#define MVM_LL_PERIOD (10 * HZ) +void iwl_mvm_tcm_work(struct work_struct *work); +void iwl_mvm_recalc_tcm(struct iwl_mvm *mvm); +void iwl_mvm_pause_tcm(struct iwl_mvm *mvm, bool with_cancel); +void iwl_mvm_resume_tcm(struct iwl_mvm *mvm); +void iwl_mvm_tcm_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +void iwl_mvm_tcm_rm_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +u8 iwl_mvm_tcm_load_percentage(u32 airtime, u32 elapsed); + void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error); unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 5bfe5306524c..cf48517944ec 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -35,6 +36,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -76,9 +78,7 @@ #include "fw/acpi.h" /* Default NVM size to read */ -#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024) -#define IWL_MAX_NVM_SECTION_SIZE 0x1b58 -#define IWL_MAX_EXT_NVM_SECTION_SIZE 0x1ffc +#define IWL_NVM_DEFAULT_CHUNK_SIZE (2 * 1024) #define NVM_WRITE_OPCODE 1 #define NVM_READ_OPCODE 0 @@ -229,19 +229,6 @@ static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section, return 0; } -static void iwl_mvm_nvm_fixups(struct iwl_mvm *mvm, unsigned int section, - u8 *data, unsigned int len) -{ -#define IWL_4165_DEVICE_ID 0x5501 -#define NVM_SKU_CAP_MIMO_DISABLE BIT(5) - - if (section == NVM_SECTION_TYPE_PHY_SKU && - mvm->trans->hw_id == IWL_4165_DEVICE_ID && data && len >= 5 && - (data[4] & NVM_SKU_CAP_MIMO_DISABLE)) - /* OTP 0x52 bug work around: it's a 1x1 device */ - data[3] = ANT_B | (ANT_B << 4); -} - /* * Reads an NVM section completely. * NICs prior to 7000 family doesn't have a real NVM, but just read @@ -282,7 +269,7 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, offset += ret; } - iwl_mvm_nvm_fixups(mvm, section, data, offset); + iwl_nvm_fixups(mvm->trans->hw_id, section, data, offset); IWL_DEBUG_EEPROM(mvm->trans->dev, "NVM section %d read completed\n", section); @@ -355,184 +342,6 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) lar_enabled); } -#define MAX_NVM_FILE_LEN 16384 - -/* - * Reads external NVM from a file into mvm->nvm_sections - * - * HOW TO CREATE THE NVM FILE FORMAT: - * ------------------------------ - * 1. create hex file, format: - * 3800 -> header - * 0000 -> header - * 5a40 -> data - * - * rev - 6 bit (word1) - * len - 10 bit (word1) - * id - 4 bit (word2) - * rsv - 12 bit (word2) - * - * 2. flip 8bits with 8 bits per line to get the right NVM file format - * - * 3. create binary file from the hex file - * - * 4. save as "iNVM_xxx.bin" under /lib/firmware - */ -int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) -{ - int ret, section_size; - u16 section_id; - const struct firmware *fw_entry; - const struct { - __le16 word1; - __le16 word2; - u8 data[]; - } *file_sec; - const u8 *eof; - u8 *temp; - int max_section_size; - const __le32 *dword_buff; - -#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF)) -#define NVM_WORD2_ID(x) (x >> 12) -#define EXT_NVM_WORD2_LEN(x) (2 * (((x) & 0xFF) << 8 | (x) >> 8)) -#define EXT_NVM_WORD1_ID(x) ((x) >> 4) -#define NVM_HEADER_0 (0x2A504C54) -#define NVM_HEADER_1 (0x4E564D2A) -#define NVM_HEADER_SIZE (4 * sizeof(u32)) - - IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n"); - - /* Maximal size depends on NVM version */ - if (mvm->trans->cfg->nvm_type != IWL_NVM_EXT) - max_section_size = IWL_MAX_NVM_SECTION_SIZE; - else - max_section_size = IWL_MAX_EXT_NVM_SECTION_SIZE; - - /* - * Obtain NVM image via request_firmware. Since we already used - * request_firmware_nowait() for the firmware binary load and only - * get here after that we assume the NVM request can be satisfied - * synchronously. - */ - ret = request_firmware(&fw_entry, mvm->nvm_file_name, - mvm->trans->dev); - if (ret) { - IWL_ERR(mvm, "ERROR: %s isn't available %d\n", - mvm->nvm_file_name, ret); - return ret; - } - - IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n", - mvm->nvm_file_name, fw_entry->size); - - if (fw_entry->size > MAX_NVM_FILE_LEN) { - IWL_ERR(mvm, "NVM file too large\n"); - ret = -EINVAL; - goto out; - } - - eof = fw_entry->data + fw_entry->size; - dword_buff = (__le32 *)fw_entry->data; - - /* some NVM file will contain a header. - * The header is identified by 2 dwords header as follow: - * dword[0] = 0x2A504C54 - * dword[1] = 0x4E564D2A - * - * This header must be skipped when providing the NVM data to the FW. - */ - if (fw_entry->size > NVM_HEADER_SIZE && - dword_buff[0] == cpu_to_le32(NVM_HEADER_0) && - dword_buff[1] == cpu_to_le32(NVM_HEADER_1)) { - file_sec = (void *)(fw_entry->data + NVM_HEADER_SIZE); - IWL_INFO(mvm, "NVM Version %08X\n", le32_to_cpu(dword_buff[2])); - IWL_INFO(mvm, "NVM Manufacturing date %08X\n", - le32_to_cpu(dword_buff[3])); - - /* nvm file validation, dword_buff[2] holds the file version */ - if (mvm->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 && - CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_C_STEP && - le32_to_cpu(dword_buff[2]) < 0xE4A) { - ret = -EFAULT; - goto out; - } - } else { - file_sec = (void *)fw_entry->data; - } - - while (true) { - if (file_sec->data > eof) { - IWL_ERR(mvm, - "ERROR - NVM file too short for section header\n"); - ret = -EINVAL; - break; - } - - /* check for EOF marker */ - if (!file_sec->word1 && !file_sec->word2) { - ret = 0; - break; - } - - if (mvm->trans->cfg->nvm_type != IWL_NVM_EXT) { - section_size = - 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1)); - section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2)); - } else { - section_size = 2 * EXT_NVM_WORD2_LEN( - le16_to_cpu(file_sec->word2)); - section_id = EXT_NVM_WORD1_ID( - le16_to_cpu(file_sec->word1)); - } - - if (section_size > max_section_size) { - IWL_ERR(mvm, "ERROR - section too large (%d)\n", - section_size); - ret = -EINVAL; - break; - } - - if (!section_size) { - IWL_ERR(mvm, "ERROR - section empty\n"); - ret = -EINVAL; - break; - } - - if (file_sec->data + section_size > eof) { - IWL_ERR(mvm, - "ERROR - NVM file too short for section (%d bytes)\n", - section_size); - ret = -EINVAL; - break; - } - - if (WARN(section_id >= NVM_MAX_NUM_SECTIONS, - "Invalid NVM section ID %d\n", section_id)) { - ret = -EINVAL; - break; - } - - temp = kmemdup(file_sec->data, section_size, GFP_KERNEL); - if (!temp) { - ret = -ENOMEM; - break; - } - - iwl_mvm_nvm_fixups(mvm, section_id, temp, section_size); - - kfree(mvm->nvm_sections[section_id].data); - mvm->nvm_sections[section_id].data = temp; - mvm->nvm_sections[section_id].length = section_size; - - /* advance to the next section */ - file_sec = (void *)(file_sec->data + section_size); - } -out: - release_firmware(fw_entry); - return ret; -} - /* Loads the NVM data stored in mvm->nvm_sections into the NIC */ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm) { @@ -585,7 +394,7 @@ int iwl_nvm_init(struct iwl_mvm *mvm) break; } - iwl_mvm_nvm_fixups(mvm, section, temp, ret); + iwl_nvm_fixups(mvm->trans->hw_id, section, temp, ret); mvm->nvm_sections[section].data = temp; mvm->nvm_sections[section].length = ret; @@ -624,14 +433,17 @@ int iwl_nvm_init(struct iwl_mvm *mvm) /* Only if PNVM selected in the mod param - load external NVM */ if (mvm->nvm_file_name) { /* read External NVM file from the mod param */ - ret = iwl_mvm_read_external_nvm(mvm); + ret = iwl_read_external_nvm(mvm->trans, mvm->nvm_file_name, + mvm->nvm_sections); if (ret) { mvm->nvm_file_name = nvm_file_C; if ((ret == -EFAULT || ret == -ENOENT) && mvm->nvm_file_name) { /* in case nvm file was failed try again */ - ret = iwl_mvm_read_external_nvm(mvm); + ret = iwl_read_external_nvm(mvm->trans, + mvm->nvm_file_name, + mvm->nvm_sections); if (ret) return ret; } else { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 224bfa1bcf53..ff1e518096c5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -250,6 +250,9 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, RX_HANDLER_SYNC), RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, RX_HANDLER_SYNC), + RX_HANDLER_GRP(DATA_PATH_GROUP, TLC_MNG_UPDATE_NOTIF, + iwl_mvm_tlc_update_notif, RX_HANDLER_SYNC), + RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, RX_HANDLER_ASYNC_LOCKED), RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, @@ -667,6 +670,12 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev); + spin_lock_init(&mvm->tcm.lock); + INIT_DELAYED_WORK(&mvm->tcm.work, iwl_mvm_tcm_work); + mvm->tcm.ts = jiffies; + mvm->tcm.ll_ts = jiffies; + mvm->tcm.uapsd_nonagg_ts = jiffies; + INIT_DELAYED_WORK(&mvm->cs_tx_unblock_dwork, iwl_mvm_tx_unblock_dwork); /* @@ -730,6 +739,9 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, sizeof(trans->dbg_conf_tlv)); trans->dbg_trigger_tlv = mvm->fw->dbg_trigger_tlv; + trans->iml = mvm->fw->iml; + trans->iml_len = mvm->fw->iml_len; + /* set up notification wait support */ iwl_notification_wait_init(&mvm->notif_wait); @@ -859,6 +871,8 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++) kfree(mvm->nvm_sections[i].data); + cancel_delayed_work_sync(&mvm->tcm.work); + iwl_mvm_tof_clean(mvm); mutex_destroy(&mvm->mutex); @@ -1026,8 +1040,6 @@ static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode, iwl_mvm_rx_queue_notif(mvm, rxb, 0); else if (cmd == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE)) iwl_mvm_rx_frame_release(mvm, napi, rxb, 0); - else if (cmd == WIDE_ID(DATA_PATH_GROUP, TLC_MNG_UPDATE_NOTIF)) - iwl_mvm_tlc_update_notif(mvm, pkt); else iwl_mvm_rx_common(mvm, rxb, pkt); } @@ -1432,6 +1444,7 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) mvm->d0i3_offloading = false; } + iwl_mvm_pause_tcm(mvm, true); /* make sure we have no running tx while configuring the seqno */ synchronize_net(); @@ -1615,6 +1628,7 @@ out: /* the FW might have updated the regdomain */ iwl_mvm_update_changed_regdom(mvm); + iwl_mvm_resume_tcm(mvm); iwl_mvm_unref(mvm, IWL_MVM_REF_EXIT_WORK); mutex_unlock(&mvm->mutex); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c index fb5745660509..b8b2b819e8e7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -26,6 +27,7 @@ * BSD LICENSE * * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -65,14 +67,14 @@ static u8 rs_fw_bw_from_sta_bw(struct ieee80211_sta *sta) { switch (sta->bandwidth) { case IEEE80211_STA_RX_BW_160: - return IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ; + return IWL_TLC_MNG_CH_WIDTH_160MHZ; case IEEE80211_STA_RX_BW_80: - return IWL_TLC_MNG_MAX_CH_WIDTH_80MHZ; + return IWL_TLC_MNG_CH_WIDTH_80MHZ; case IEEE80211_STA_RX_BW_40: - return IWL_TLC_MNG_MAX_CH_WIDTH_40MHZ; + return IWL_TLC_MNG_CH_WIDTH_40MHZ; case IEEE80211_STA_RX_BW_20: default: - return IWL_TLC_MNG_MAX_CH_WIDTH_20MHZ; + return IWL_TLC_MNG_CH_WIDTH_20MHZ; } } @@ -85,7 +87,9 @@ static u8 rs_fw_set_active_chains(u8 chains) if (chains & ANT_B) fw_chains |= IWL_TLC_MNG_CHAIN_B_MSK; if (chains & ANT_C) - fw_chains |= IWL_TLC_MNG_CHAIN_C_MSK; + WARN(false, + "tlc offload doesn't support antenna C. chains: 0x%x\n", + chains); return fw_chains; } @@ -97,13 +101,13 @@ static u8 rs_fw_sgi_cw_support(struct ieee80211_sta *sta) u8 supp = 0; if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20) - supp |= IWL_TLC_MNG_SGI_20MHZ_MSK; + supp |= BIT(IWL_TLC_MNG_CH_WIDTH_20MHZ); if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40) - supp |= IWL_TLC_MNG_SGI_40MHZ_MSK; + supp |= BIT(IWL_TLC_MNG_CH_WIDTH_40MHZ); if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80) - supp |= IWL_TLC_MNG_SGI_80MHZ_MSK; + supp |= BIT(IWL_TLC_MNG_CH_WIDTH_80MHZ); if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160) - supp |= IWL_TLC_MNG_SGI_160MHZ_MSK; + supp |= BIT(IWL_TLC_MNG_CH_WIDTH_160MHZ); return supp; } @@ -114,9 +118,7 @@ static u16 rs_fw_set_config_flags(struct iwl_mvm *mvm, struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; bool vht_ena = vht_cap && vht_cap->vht_supported; - u16 flags = IWL_TLC_MNG_CFG_FLAGS_CCK_MSK | - IWL_TLC_MNG_CFG_FLAGS_DCM_MSK | - IWL_TLC_MNG_CFG_FLAGS_DD_MSK; + u16 flags = 0; if (mvm->cfg->ht_params->stbc && (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && @@ -129,16 +131,11 @@ static u16 rs_fw_set_config_flags(struct iwl_mvm *mvm, (vht_ena && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)))) flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_BEAMFORMER) && - (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && - (vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) - flags |= IWL_TLC_MNG_CFG_FLAGS_BF_MSK; - return flags; } static -int rs_fw_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap, +int rs_fw_vht_highest_rx_mcs_index(const struct ieee80211_sta_vht_cap *vht_cap, int nss) { u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) & @@ -160,15 +157,16 @@ int rs_fw_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap, return 0; } -static void rs_fw_vht_set_enabled_rates(struct ieee80211_sta *sta, - struct ieee80211_sta_vht_cap *vht_cap, - struct iwl_tlc_config_cmd *cmd) +static void +rs_fw_vht_set_enabled_rates(const struct ieee80211_sta *sta, + const struct ieee80211_sta_vht_cap *vht_cap, + struct iwl_tlc_config_cmd *cmd) { u16 supp; int i, highest_mcs; for (i = 0; i < sta->rx_nss; i++) { - if (i == MAX_RS_ANT_NUM) + if (i == MAX_NSS) break; highest_mcs = rs_fw_vht_highest_rx_mcs_index(vht_cap, i + 1); @@ -179,7 +177,9 @@ static void rs_fw_vht_set_enabled_rates(struct ieee80211_sta *sta, if (sta->bandwidth == IEEE80211_STA_RX_BW_20) supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9); - cmd->ht_supp_rates[i] = cpu_to_le16(supp); + cmd->ht_rates[i][0] = cpu_to_le16(supp); + if (sta->bandwidth == IEEE80211_STA_RX_BW_160) + cmd->ht_rates[i][1] = cmd->ht_rates[i][0]; } } @@ -190,8 +190,8 @@ static void rs_fw_set_supp_rates(struct ieee80211_sta *sta, int i; unsigned long tmp; unsigned long supp; /* must be unsigned long for for_each_set_bit */ - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; + const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; /* non HT rates */ supp = 0; @@ -199,45 +199,40 @@ static void rs_fw_set_supp_rates(struct ieee80211_sta *sta, for_each_set_bit(i, &tmp, BITS_PER_LONG) supp |= BIT(sband->bitrates[i].hw_value); - cmd->non_ht_supp_rates = cpu_to_le16(supp); + cmd->non_ht_rates = cpu_to_le16(supp); cmd->mode = IWL_TLC_MNG_MODE_NON_HT; - /* HT/VHT rates */ if (vht_cap && vht_cap->vht_supported) { cmd->mode = IWL_TLC_MNG_MODE_VHT; rs_fw_vht_set_enabled_rates(sta, vht_cap, cmd); } else if (ht_cap && ht_cap->ht_supported) { cmd->mode = IWL_TLC_MNG_MODE_HT; - cmd->ht_supp_rates[0] = cpu_to_le16(ht_cap->mcs.rx_mask[0]); - cmd->ht_supp_rates[1] = cpu_to_le16(ht_cap->mcs.rx_mask[1]); + cmd->ht_rates[0][0] = cpu_to_le16(ht_cap->mcs.rx_mask[0]); + cmd->ht_rates[1][0] = cpu_to_le16(ht_cap->mcs.rx_mask[1]); } } -static void rs_fw_tlc_mng_notif_req_config(struct iwl_mvm *mvm, u8 sta_id) -{ - u32 cmd_id = iwl_cmd_id(TLC_MNG_NOTIF_REQ_CMD, DATA_PATH_GROUP, 0); - struct iwl_tlc_notif_req_config_cmd cfg_cmd = { - .sta_id = sta_id, - .flags = cpu_to_le16(IWL_TLC_NOTIF_INIT_RATE_MSK), - .interval = cpu_to_le16(IWL_TLC_NOTIF_REQ_INTERVAL), - }; - int ret; - - ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cfg_cmd), &cfg_cmd); - if (ret) - IWL_ERR(mvm, "Failed to send TLC notif request (%d)\n", ret); -} - -void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) +void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) { + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_tlc_update_notif *notif; + struct ieee80211_sta *sta; struct iwl_mvm_sta *mvmsta; struct iwl_lq_sta_rs_fw *lq_sta; + u32 flags; rcu_read_lock(); notif = (void *)pkt->data; - mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, notif->sta_id); + sta = rcu_dereference(mvm->fw_id_to_mac_id[notif->sta_id]); + if (IS_ERR_OR_NULL(sta)) { + IWL_ERR(mvm, "Invalid sta id (%d) in FW TLC notification\n", + notif->sta_id); + goto out; + } + + mvmsta = iwl_mvm_sta_from_mac80211(sta); if (!mvmsta) { IWL_ERR(mvm, "Invalid sta id (%d) in FW TLC notification\n", @@ -245,14 +240,30 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) goto out; } + flags = le32_to_cpu(notif->flags); + lq_sta = &mvmsta->lq_sta.rs_fw; - if (le16_to_cpu(notif->flags) & IWL_TLC_NOTIF_INIT_RATE_MSK) { - lq_sta->last_rate_n_flags = - le32_to_cpu(notif->values[IWL_TLC_NOTIF_INIT_RATE_POS]); + if (flags & IWL_TLC_NOTIF_FLAG_RATE) { + lq_sta->last_rate_n_flags = le32_to_cpu(notif->rate); IWL_DEBUG_RATE(mvm, "new rate_n_flags: 0x%X\n", lq_sta->last_rate_n_flags); } + + if (flags & IWL_TLC_NOTIF_FLAG_AMSDU) { + u16 size = le32_to_cpu(notif->amsdu_size); + + if (WARN_ON(sta->max_amsdu_len < size)) + goto out; + + mvmsta->amsdu_enabled = le32_to_cpu(notif->amsdu_enabled); + mvmsta->max_amsdu_len = size; + + IWL_DEBUG_RATE(mvm, + "AMSDU update. AMSDU size: %d, AMSDU selected size: %d, AMSDU TID bitmap 0x%X\n", + le32_to_cpu(notif->amsdu_size), size, + mvmsta->amsdu_enabled); + } out: rcu_read_unlock(); } @@ -267,12 +278,12 @@ void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct ieee80211_supported_band *sband; struct iwl_tlc_config_cmd cfg_cmd = { .sta_id = mvmsta->sta_id, - .max_supp_ch_width = rs_fw_bw_from_sta_bw(sta), + .max_ch_width = rs_fw_bw_from_sta_bw(sta), .flags = cpu_to_le16(rs_fw_set_config_flags(mvm, sta)), .chains = rs_fw_set_active_chains(iwl_mvm_get_valid_tx_ant(mvm)), - .max_supp_ss = sta->rx_nss, - .max_ampdu_cnt = cpu_to_le32(mvmsta->max_agg_bufsize), + .max_mpdu_len = cpu_to_le16(sta->max_amsdu_len), .sgi_ch_width_supp = rs_fw_sgi_cw_support(sta), + .amsdu = iwl_mvm_is_csum_supported(mvm), }; int ret; @@ -287,8 +298,6 @@ void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cfg_cmd), &cfg_cmd); if (ret) IWL_ERR(mvm, "Failed to send rate scale config (%d)\n", ret); - - rs_fw_tlc_mng_notif_req_config(mvm, cfg_cmd.sta_id); } int rs_fw_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 5d776ec1840f..642da10b0b7f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -3,6 +3,7 @@ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -13,10 +14,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * @@ -651,9 +648,10 @@ static void rs_tl_turn_on_agg(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, } tid_data = &mvmsta->tid_data[tid]; - if ((tid_data->state == IWL_AGG_OFF) && + if (mvmsta->sta_state >= IEEE80211_STA_AUTHORIZED && + tid_data->state == IWL_AGG_OFF && (lq_sta->tx_agg_tid_en & BIT(tid)) && - (tid_data->tx_count_last >= IWL_MVM_RS_AGG_START_THRESHOLD)) { + tid_data->tx_count_last >= IWL_MVM_RS_AGG_START_THRESHOLD) { IWL_DEBUG_RATE(mvm, "try to aggregate tid %d\n", tid); if (rs_tl_turn_on_agg_for_tid(mvm, lq_sta, tid, sta) == 0) tid_data->state = IWL_AGG_QUEUED; @@ -1257,7 +1255,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, (unsigned long)(lq_sta->last_tx + (IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) { IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n"); - iwl_mvm_rs_rate_init(mvm, sta, info->band, false); + iwl_mvm_rs_rate_init(mvm, sta, info->band); return; } lq_sta->last_tx = jiffies; @@ -1715,12 +1713,18 @@ static void rs_set_amsdu_len(struct iwl_mvm *mvm, struct ieee80211_sta *sta, { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + /* + * In case TLC offload is not active amsdu_enabled is either 0xFFFF + * or 0, since there is no per-TID alg. + */ if ((!is_vht(&tbl->rate) && !is_ht(&tbl->rate)) || tbl->rate.index < IWL_RATE_MCS_5_INDEX || scale_action == RS_ACTION_DOWNSCALE) - mvmsta->tlc_amsdu = false; + mvmsta->amsdu_enabled = 0; else - mvmsta->tlc_amsdu = true; + mvmsta->amsdu_enabled = 0xFFFF; + + mvmsta->max_amsdu_len = sta->max_amsdu_len; } /* @@ -2684,9 +2688,9 @@ static void rs_get_initial_rate(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, enum nl80211_band band, - struct rs_rate *rate, - bool init) + struct rs_rate *rate) { + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); int i, nentries; unsigned long active_rate; s8 best_rssi = S8_MIN; @@ -2748,7 +2752,8 @@ static void rs_get_initial_rate(struct iwl_mvm *mvm, * bandwidth rate, and after authorization, when the phy context * is already up-to-date, re-init rs with the correct bw. */ - u32 bw = init ? RATE_MCS_CHAN_WIDTH_20 : rs_bw_from_sta_bw(sta); + u32 bw = mvmsta->sta_state < IEEE80211_STA_AUTHORIZED ? + RATE_MCS_CHAN_WIDTH_20 : rs_bw_from_sta_bw(sta); switch (bw) { case RATE_MCS_CHAN_WIDTH_40: @@ -2833,9 +2838,9 @@ void rs_update_last_rssi(struct iwl_mvm *mvm, static void rs_initialize_lq(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, - enum nl80211_band band, - bool init) + enum nl80211_band band) { + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_scale_tbl_info *tbl; struct rs_rate *rate; u8 active_tbl = 0; @@ -2851,7 +2856,7 @@ static void rs_initialize_lq(struct iwl_mvm *mvm, tbl = &(lq_sta->lq_info[active_tbl]); rate = &tbl->rate; - rs_get_initial_rate(mvm, sta, lq_sta, band, rate, init); + rs_get_initial_rate(mvm, sta, lq_sta, band, rate); rs_init_optimal_rate(mvm, sta, lq_sta); WARN_ONCE(rate->ant != ANT_A && rate->ant != ANT_B, @@ -2864,7 +2869,8 @@ static void rs_initialize_lq(struct iwl_mvm *mvm, rs_set_expected_tpt_table(lq_sta, tbl); rs_fill_lq_cmd(mvm, sta, lq_sta, rate); /* TODO restore station should remember the lq cmd */ - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, init); + iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, + mvmsta->sta_state < IEEE80211_STA_AUTHORIZED); } static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta, @@ -3117,7 +3123,7 @@ void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg) * Called after adding a new station to initialize rate scaling */ static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - enum nl80211_band band, bool init) + enum nl80211_band band) { int i, j; struct ieee80211_hw *hw = mvm->hw; @@ -3134,7 +3140,8 @@ static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, sband = hw->wiphy->bands[band]; lq_sta->lq.sta_id = mvmsta->sta_id; - mvmsta->tlc_amsdu = false; + mvmsta->amsdu_enabled = 0; + mvmsta->max_amsdu_len = sta->max_amsdu_len; for (j = 0; j < LQ_SIZE; j++) rs_rate_scale_clear_tbl_windows(mvm, &lq_sta->lq_info[j]); @@ -3196,7 +3203,7 @@ static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, #ifdef CONFIG_IWLWIFI_DEBUGFS iwl_mvm_reset_frame_stats(mvm); #endif - rs_initialize_lq(mvm, sta, lq_sta, band, init); + rs_initialize_lq(mvm, sta, lq_sta, band); } static void rs_drv_rate_update(void *mvm_r, @@ -3216,7 +3223,7 @@ static void rs_drv_rate_update(void *mvm_r, for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) ieee80211_stop_tx_ba_session(sta, tid); - iwl_mvm_rs_rate_init(mvm, sta, sband->band, false); + iwl_mvm_rs_rate_init(mvm, sta, sband->band); } #ifdef CONFIG_MAC80211_DEBUGFS @@ -3744,7 +3751,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, (rate->sgi) ? "SGI" : "NGI", (rate->ldpc) ? "LDPC" : "BCC", (lq_sta->is_agg) ? "AGG on" : "", - (mvmsta->tlc_amsdu) ? "AMSDU on" : ""); + (mvmsta->amsdu_enabled) ? "AMSDU on" : ""); } desc += scnprintf(buff + desc, bufsz - desc, "last tx rate=0x%X\n", lq_sta->last_rate_n_flags); @@ -4062,12 +4069,12 @@ static const struct rate_control_ops rs_mvm_ops_drv = { }; void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - enum nl80211_band band, bool init) + enum nl80211_band band) { if (iwl_mvm_has_tlc_offload(mvm)) rs_fw_rate_init(mvm, sta, band); else - rs_drv_rate_init(mvm, sta, band, init); + rs_drv_rate_init(mvm, sta, band); } int iwl_mvm_rate_control_register(void) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h index fb18cb8c233d..cffb8c852934 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h @@ -3,6 +3,7 @@ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2015 Intel Mobile Communications GmbH * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -13,10 +14,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * @@ -410,7 +407,7 @@ struct iwl_lq_sta { /* Initialize station's rate scaling information after adding station */ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - enum nl80211_band band, bool init); + enum nl80211_band band); /* Notify RS about Tx status */ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, @@ -454,5 +451,6 @@ void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, enum nl80211_band band); int rs_fw_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, bool enable); -void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt); +void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); #endif /* __rs__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index d26833c5ce1f..bfb163419c67 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -254,6 +254,74 @@ static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm, return 0; } +static void iwl_mvm_rx_handle_tcm(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct ieee80211_hdr *hdr, u32 len, + struct iwl_rx_phy_info *phy_info, + u32 rate_n_flags) +{ + struct iwl_mvm_sta *mvmsta; + struct iwl_mvm_tcm_mac *mdata; + int mac; + int ac = IEEE80211_AC_BE; /* treat non-QoS as BE */ + struct iwl_mvm_vif *mvmvif; + /* expected throughput in 100Kbps, single stream, 20 MHz */ + static const u8 thresh_tpt[] = { + 9, 18, 30, 42, 60, 78, 90, 96, 120, 135, + }; + u16 thr; + + if (ieee80211_is_data_qos(hdr->frame_control)) + ac = tid_to_mac80211_ac[ieee80211_get_tid(hdr)]; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + mac = mvmsta->mac_id_n_color & FW_CTXT_ID_MSK; + + if (time_after(jiffies, mvm->tcm.ts + MVM_TCM_PERIOD)) + schedule_delayed_work(&mvm->tcm.work, 0); + mdata = &mvm->tcm.data[mac]; + mdata->rx.pkts[ac]++; + + /* count the airtime only once for each ampdu */ + if (mdata->rx.last_ampdu_ref != mvm->ampdu_ref) { + mdata->rx.last_ampdu_ref = mvm->ampdu_ref; + mdata->rx.airtime += le16_to_cpu(phy_info->frame_time); + } + + if (!(rate_n_flags & (RATE_MCS_HT_MSK | RATE_MCS_VHT_MSK))) + return; + + mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); + + if (mdata->opened_rx_ba_sessions || + mdata->uapsd_nonagg_detect.detected || + (!mvmvif->queue_params[IEEE80211_AC_VO].uapsd && + !mvmvif->queue_params[IEEE80211_AC_VI].uapsd && + !mvmvif->queue_params[IEEE80211_AC_BE].uapsd && + !mvmvif->queue_params[IEEE80211_AC_BK].uapsd) || + mvmsta->sta_id != mvmvif->ap_sta_id) + return; + + if (rate_n_flags & RATE_MCS_HT_MSK) { + thr = thresh_tpt[rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK]; + thr *= 1 + ((rate_n_flags & RATE_HT_MCS_NSS_MSK) >> + RATE_HT_MCS_NSS_POS); + } else { + if (WARN_ON((rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK) >= + ARRAY_SIZE(thresh_tpt))) + return; + thr = thresh_tpt[rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK]; + thr *= 1 + ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> + RATE_VHT_MCS_NSS_POS); + } + + thr <<= ((rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) >> + RATE_MCS_CHAN_WIDTH_POS); + + mdata->uapsd_nonagg_detect.rx_bytes += len; + ewma_rate_add(&mdata->uapsd_nonagg_detect.rate, thr); +} + static void iwl_mvm_rx_csum(struct ieee80211_sta *sta, struct sk_buff *skb, u32 status) @@ -408,6 +476,12 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, NULL); } + if (!mvm->tcm.paused && len >= sizeof(*hdr) && + !is_multicast_ether_addr(hdr->addr1) && + ieee80211_is_data(hdr->frame_control)) + iwl_mvm_rx_handle_tcm(mvm, sta, hdr, len, phy_info, + rate_n_flags); + if (ieee80211_is_data(hdr->frame_control)) iwl_mvm_rx_csum(sta, skb, rx_pkt_status); } @@ -654,7 +728,8 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, int expected_size; int i; u8 *energy; - __le32 *bytes, *air_time; + __le32 *bytes; + __le32 *air_time; __le32 flags; if (!iwl_mvm_has_new_rx_stats_api(mvm)) { @@ -752,6 +827,32 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, sta->avg_energy = energy[i]; } rcu_read_unlock(); + + /* + * Don't update in case the statistics are not cleared, since + * we will end up counting twice the same airtime, once in TCM + * request and once in statistics notification. + */ + if (!(le32_to_cpu(flags) & IWL_STATISTICS_REPLY_FLG_CLEAR)) + return; + + spin_lock(&mvm->tcm.lock); + for (i = 0; i < NUM_MAC_INDEX_DRIVER; i++) { + struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[i]; + u32 airtime = le32_to_cpu(air_time[i]); + u32 rx_bytes = le32_to_cpu(bytes[i]); + + mdata->uapsd_nonagg_detect.rx_bytes += rx_bytes; + if (airtime) { + /* re-init every time to store rate from FW */ + ewma_rate_init(&mdata->uapsd_nonagg_detect.rate); + ewma_rate_add(&mdata->uapsd_nonagg_detect.rate, + rx_bytes * 8 / airtime); + } + + mdata->rx.airtime += airtime; + } + spin_unlock(&mvm->tcm.lock); } void iwl_mvm_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 4a4ccfd11e5b..129c4c09648d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -112,7 +112,7 @@ static inline int iwl_mvm_check_pn(struct iwl_mvm *mvm, struct sk_buff *skb, return -1; if (ieee80211_is_data_qos(hdr->frame_control)) - tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; + tid = ieee80211_get_tid(hdr); else tid = 0; @@ -151,17 +151,9 @@ static void iwl_mvm_create_skb(struct sk_buff *skb, struct ieee80211_hdr *hdr, unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control); if (desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD) { + len -= 2; pad_len = 2; - - /* - * If the device inserted padding it means that (it thought) - * the 802.11 header wasn't a multiple of 4 bytes long. In - * this case, reserve two bytes at the start of the SKB to - * align the payload properly in case we end up copying it. - */ - skb_reserve(skb, pad_len); } - len -= pad_len; /* If frame is small enough to fit in skb->head, pull it completely. * If not, only pull ieee80211_hdr (including crypto if present, and @@ -235,12 +227,24 @@ static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, } static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, - struct ieee80211_rx_status *stats, - struct iwl_rx_mpdu_desc *desc, u32 pkt_flags, - int queue, u8 *crypt_len) + struct ieee80211_rx_status *stats, u16 phy_info, + struct iwl_rx_mpdu_desc *desc, + u32 pkt_flags, int queue, u8 *crypt_len) { u16 status = le16_to_cpu(desc->status); + /* + * Drop UNKNOWN frames in aggregation, unless in monitor mode + * (where we don't have the keys). + * We limit this to aggregation because in TKIP this is a valid + * scenario, since we may not have the (correct) TTAK (phase 1 + * key) in the firmware. + */ + if (phy_info & IWL_RX_MPDU_PHY_AMPDU && + (status & IWL_RX_MPDU_STATUS_SEC_MASK) == + IWL_RX_MPDU_STATUS_SEC_UNKNOWN && !mvm->monitor_on) + return -1; + if (!ieee80211_has_protected(hdr->frame_control) || (status & IWL_RX_MPDU_STATUS_SEC_MASK) == IWL_RX_MPDU_STATUS_SEC_NONE) @@ -347,8 +351,7 @@ static bool iwl_mvm_is_dup(struct ieee80211_sta *sta, int queue, if (ieee80211_is_data_qos(hdr->frame_control)) /* frame has qos control */ - tid = *ieee80211_get_qos_ctl(hdr) & - IEEE80211_QOS_CTL_TID_MASK; + tid = ieee80211_get_tid(hdr); else tid = IWL_MAX_TID_COUNT; @@ -587,14 +590,10 @@ void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, notif = (void *)pkt->data; internal_notif = (void *)notif->payload; - if (internal_notif->sync) { - if (mvm->queue_sync_cookie != internal_notif->cookie) { - WARN_ONCE(1, - "Received expired RX queue sync message\n"); - return; - } - if (!atomic_dec_return(&mvm->queue_sync_counter)) - wake_up(&mvm->rx_sync_waitq); + if (internal_notif->sync && + mvm->queue_sync_cookie != internal_notif->cookie) { + WARN_ONCE(1, "Received expired RX queue sync message\n"); + return; } switch (internal_notif->type) { @@ -606,6 +605,10 @@ void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, default: WARN_ONCE(1, "Invalid identifier %d", internal_notif->type); } + + if (internal_notif->sync && + !atomic_dec_return(&mvm->queue_sync_counter)) + wake_up(&mvm->rx_sync_waitq); } /* @@ -628,7 +631,7 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, bool amsdu = desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU; bool last_subframe = desc->amsdu_info & IWL_RX_MPDU_AMSDU_LAST_SUBFRAME; - u8 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; + u8 tid = ieee80211_get_tid(hdr); u8 sub_frame_idx = desc->amsdu_info & IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK; struct iwl_mvm_reorder_buf_entry *entries; @@ -867,9 +870,19 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, return; } + if (desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD) { + /* + * If the device inserted padding it means that (it thought) + * the 802.11 header wasn't a multiple of 4 bytes long. In + * this case, reserve two bytes at the start of the SKB to + * align the payload properly in case we end up copying it. + */ + skb_reserve(skb, 2); + } + rx_status = IEEE80211_SKB_RXCB(skb); - if (iwl_mvm_rx_crypto(mvm, hdr, rx_status, desc, + if (iwl_mvm_rx_crypto(mvm, hdr, rx_status, phy_info, desc, le32_to_cpu(pkt->len_n_flags), queue, &crypt_len)) { kfree_skb(skb); @@ -941,6 +954,12 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, IWL_RX_MPDU_REORDER_BAID_MASK) >> IWL_RX_MPDU_REORDER_BAID_SHIFT); + if (!mvm->tcm.paused && len >= sizeof(*hdr) && + !is_multicast_ether_addr(hdr->addr1) && + ieee80211_is_data(hdr->frame_control) && + time_after(jiffies, mvm->tcm.ts + MVM_TCM_PERIOD)) + schedule_delayed_work(&mvm->tcm.work, 0); + /* * We have tx blocked stations (with CS bit). If we heard * frames from a blocked station on a new channel we can diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index b31f0ffbbbf0..4b3753d78d03 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -19,9 +20,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA + * along with this program * * The full GNU General Public License is included in this distribution * in the file called COPYING. @@ -76,12 +75,6 @@ #define IWL_DENSE_EBS_SCAN_RATIO 5 #define IWL_SPARSE_EBS_SCAN_RATIO 1 -enum iwl_mvm_traffic_load { - IWL_MVM_TRAFFIC_LOW, - IWL_MVM_TRAFFIC_MEDIUM, - IWL_MVM_TRAFFIC_HIGH, -}; - #define IWL_SCAN_DWELL_ACTIVE 10 #define IWL_SCAN_DWELL_PASSIVE 110 #define IWL_SCAN_DWELL_FRAGMENTED 44 @@ -123,7 +116,9 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = { }; struct iwl_mvm_scan_params { + /* For CDB this is low band scan type, for non-CDB - type. */ enum iwl_mvm_scan_type type; + enum iwl_mvm_scan_type hb_type; u32 n_channels; u16 delay; int n_ssids; @@ -152,7 +147,7 @@ static inline void *iwl_mvm_get_scan_req_umac_data(struct iwl_mvm *mvm) if (iwl_mvm_is_adaptive_dwell_supported(mvm)) return (void *)&cmd->v7.data; - if (iwl_mvm_has_new_tx_api(mvm)) + if (iwl_mvm_cdb_scan_api(mvm)) return (void *)&cmd->v6.data; return (void *)&cmd->v1.data; @@ -169,7 +164,7 @@ iwl_mvm_get_scan_req_umac_channel(struct iwl_mvm *mvm) if (iwl_mvm_is_adaptive_dwell_supported(mvm)) return &cmd->v7.channel; - if (iwl_mvm_has_new_tx_api(mvm)) + if (iwl_mvm_cdb_scan_api(mvm)) return &cmd->v6.channel; return &cmd->v1.channel; @@ -234,15 +229,21 @@ static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac, static enum iwl_mvm_traffic_load iwl_mvm_get_traffic_load(struct iwl_mvm *mvm) { - return IWL_MVM_TRAFFIC_LOW; + return mvm->tcm.result.global_load; +} + +static enum iwl_mvm_traffic_load +iwl_mvm_get_traffic_load_band(struct iwl_mvm *mvm, enum nl80211_band band) +{ + return mvm->tcm.result.band_load[band]; } static enum -iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, bool p2p_device) +iwl_mvm_scan_type _iwl_mvm_get_scan_type(struct iwl_mvm *mvm, bool p2p_device, + enum iwl_mvm_traffic_load load, + bool low_latency) { int global_cnt = 0; - enum iwl_mvm_traffic_load load; - bool low_latency; ieee80211_iterate_active_interfaces_atomic(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, @@ -251,9 +252,6 @@ iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, bool p2p_device) if (!global_cnt) return IWL_SCAN_TYPE_UNASSOC; - load = iwl_mvm_get_traffic_load(mvm); - low_latency = iwl_mvm_low_latency(mvm); - if ((load == IWL_MVM_TRAFFIC_HIGH || low_latency) && !p2p_device && fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_FRAGMENTED_SCAN)) return IWL_SCAN_TYPE_FRAGMENTED; @@ -264,25 +262,57 @@ iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, bool p2p_device) return IWL_SCAN_TYPE_WILD; } +static enum +iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, bool p2p_device) +{ + enum iwl_mvm_traffic_load load; + bool low_latency; + + load = iwl_mvm_get_traffic_load(mvm); + low_latency = iwl_mvm_low_latency(mvm); + + return _iwl_mvm_get_scan_type(mvm, p2p_device, load, low_latency); +} + +static enum +iwl_mvm_scan_type iwl_mvm_get_scan_type_band(struct iwl_mvm *mvm, + bool p2p_device, + enum nl80211_band band) +{ + enum iwl_mvm_traffic_load load; + bool low_latency; + + load = iwl_mvm_get_traffic_load_band(mvm, band); + low_latency = iwl_mvm_low_latency_band(mvm, band); + + return _iwl_mvm_get_scan_type(mvm, p2p_device, load, low_latency); +} + static int iwl_mvm_get_measurement_dwell(struct iwl_mvm *mvm, struct cfg80211_scan_request *req, struct iwl_mvm_scan_params *params) { + u32 duration = scan_timing[params->type].max_out_time; + if (!req->duration) return 0; - if (req->duration_mandatory && - req->duration > scan_timing[params->type].max_out_time) { + if (iwl_mvm_is_cdb_supported(mvm)) { + u32 hb_time = scan_timing[params->hb_type].max_out_time; + + duration = min_t(u32, duration, hb_time); + } + + if (req->duration_mandatory && req->duration > duration) { IWL_DEBUG_SCAN(mvm, "Measurement scan - too long dwell %hu (max out time %u)\n", req->duration, - scan_timing[params->type].max_out_time); + duration); return -EOPNOTSUPP; } - return min_t(u32, (u32)req->duration, - scan_timing[params->type].max_out_time); + return min_t(u32, (u32)req->duration, duration); } static inline bool iwl_mvm_rrm_scan_needed(struct iwl_mvm *mvm) @@ -437,6 +467,7 @@ void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm, ieee80211_scan_completed(mvm->hw, &info); iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); cancel_delayed_work(&mvm->scan_timeout_dwork); + iwl_mvm_resume_tcm(mvm); } else { IWL_ERR(mvm, "got scan complete notification but no scan is running\n"); @@ -1030,22 +1061,38 @@ static void iwl_mvm_fill_scan_config_v1(struct iwl_mvm *mvm, void *config, static void iwl_mvm_fill_scan_config(struct iwl_mvm *mvm, void *config, u32 flags, u8 channel_flags) { - enum iwl_mvm_scan_type type = iwl_mvm_get_scan_type(mvm, false); struct iwl_scan_config *cfg = config; cfg->flags = cpu_to_le32(flags); cfg->tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); cfg->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm)); cfg->legacy_rates = iwl_mvm_scan_config_rates(mvm); - cfg->out_of_channel_time[0] = - cpu_to_le32(scan_timing[type].max_out_time); - cfg->suspend_time[0] = cpu_to_le32(scan_timing[type].suspend_time); if (iwl_mvm_is_cdb_supported(mvm)) { - cfg->suspend_time[1] = - cpu_to_le32(scan_timing[type].suspend_time); - cfg->out_of_channel_time[1] = + enum iwl_mvm_scan_type lb_type, hb_type; + + lb_type = iwl_mvm_get_scan_type_band(mvm, false, + NL80211_BAND_2GHZ); + hb_type = iwl_mvm_get_scan_type_band(mvm, false, + NL80211_BAND_5GHZ); + + cfg->out_of_channel_time[SCAN_LB_LMAC_IDX] = + cpu_to_le32(scan_timing[lb_type].max_out_time); + cfg->suspend_time[SCAN_LB_LMAC_IDX] = + cpu_to_le32(scan_timing[lb_type].suspend_time); + + cfg->out_of_channel_time[SCAN_HB_LMAC_IDX] = + cpu_to_le32(scan_timing[hb_type].max_out_time); + cfg->suspend_time[SCAN_HB_LMAC_IDX] = + cpu_to_le32(scan_timing[hb_type].suspend_time); + } else { + enum iwl_mvm_scan_type type = + iwl_mvm_get_scan_type(mvm, false); + + cfg->out_of_channel_time[SCAN_LB_LMAC_IDX] = cpu_to_le32(scan_timing[type].max_out_time); + cfg->suspend_time[SCAN_LB_LMAC_IDX] = + cpu_to_le32(scan_timing[type].suspend_time); } iwl_mvm_fill_scan_dwell(mvm, &cfg->dwell); @@ -1065,7 +1112,8 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) struct iwl_host_cmd cmd = { .id = iwl_cmd_id(SCAN_CFG_CMD, IWL_ALWAYS_LONG_GROUP, 0), }; - enum iwl_mvm_scan_type type = iwl_mvm_get_scan_type(mvm, false); + enum iwl_mvm_scan_type type; + enum iwl_mvm_scan_type hb_type = IWL_SCAN_TYPE_NOT_SET; int num_channels = mvm->nvm_data->bands[NL80211_BAND_2GHZ].n_channels + mvm->nvm_data->bands[NL80211_BAND_5GHZ].n_channels; @@ -1075,10 +1123,20 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels)) return -ENOBUFS; - if (type == mvm->scan_type) - return 0; + if (iwl_mvm_is_cdb_supported(mvm)) { + type = iwl_mvm_get_scan_type_band(mvm, false, + NL80211_BAND_2GHZ); + hb_type = iwl_mvm_get_scan_type_band(mvm, false, + NL80211_BAND_5GHZ); + if (type == mvm->scan_type && hb_type == mvm->hb_scan_type) + return 0; + } else { + type = iwl_mvm_get_scan_type(mvm, false); + if (type == mvm->scan_type) + return 0; + } - if (iwl_mvm_has_new_tx_api(mvm)) + if (iwl_mvm_cdb_scan_api(mvm)) cmd_size = sizeof(struct iwl_scan_config); else cmd_size = sizeof(struct iwl_scan_config_v1); @@ -1107,10 +1165,15 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) IWL_CHANNEL_FLAG_EBS_ADD | IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE; - if (iwl_mvm_has_new_tx_api(mvm)) { - flags |= (type == IWL_SCAN_TYPE_FRAGMENTED) ? - SCAN_CONFIG_FLAG_SET_LMAC2_FRAGMENTED : - SCAN_CONFIG_FLAG_CLEAR_LMAC2_FRAGMENTED; + /* + * Check for fragmented scan on LMAC2 - high band. + * LMAC1 - low band is checked above. + */ + if (iwl_mvm_cdb_scan_api(mvm)) { + if (iwl_mvm_is_cdb_supported(mvm)) + flags |= (hb_type == IWL_SCAN_TYPE_FRAGMENTED) ? + SCAN_CONFIG_FLAG_SET_LMAC2_FRAGMENTED : + SCAN_CONFIG_FLAG_CLEAR_LMAC2_FRAGMENTED; iwl_mvm_fill_scan_config(mvm, cfg, flags, channel_flags); } else { iwl_mvm_fill_scan_config_v1(mvm, cfg, flags, channel_flags); @@ -1123,8 +1186,10 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) IWL_DEBUG_SCAN(mvm, "Sending UMAC scan config\n"); ret = iwl_mvm_send_cmd(mvm, &cmd); - if (!ret) + if (!ret) { mvm->scan_type = type; + mvm->hb_scan_type = hb_type; + } kfree(cfg); return ret; @@ -1178,7 +1243,7 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, cpu_to_le32(timing->suspend_time); if (iwl_mvm_is_cdb_supported(mvm)) { - hb_timing = &scan_timing[params->type]; + hb_timing = &scan_timing[params->hb_type]; cmd->v7.max_out_time[SCAN_HB_LMAC_IDX] = cpu_to_le32(hb_timing->max_out_time); @@ -1208,7 +1273,7 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, cmd->v1.fragmented_dwell = IWL_SCAN_DWELL_FRAGMENTED; if (iwl_mvm_is_cdb_supported(mvm)) { - hb_timing = &scan_timing[params->type]; + hb_timing = &scan_timing[params->hb_type]; cmd->v6.max_out_time[SCAN_HB_LMAC_IDX] = cpu_to_le32(hb_timing->max_out_time); @@ -1216,7 +1281,7 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, cpu_to_le32(hb_timing->suspend_time); } - if (iwl_mvm_has_new_tx_api(mvm)) { + if (iwl_mvm_cdb_scan_api(mvm)) { cmd->v6.scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); cmd->v6.max_out_time[SCAN_LB_LMAC_IDX] = @@ -1232,6 +1297,11 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, cpu_to_le32(timing->suspend_time); } } + + if (iwl_mvm_is_regular_scan(params)) + cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); + else + cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_2); } static void @@ -1262,11 +1332,12 @@ static u16 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm, if (params->n_ssids == 1 && params->ssids[0].ssid_len != 0) flags |= IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT; - if (params->type == IWL_SCAN_TYPE_FRAGMENTED) { + if (params->type == IWL_SCAN_TYPE_FRAGMENTED) flags |= IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED; - if (iwl_mvm_is_cdb_supported(mvm)) - flags |= IWL_UMAC_SCAN_GEN_FLAGS_LMAC2_FRAGMENTED; - } + + if (iwl_mvm_is_cdb_supported(mvm) && + params->hb_type == IWL_SCAN_TYPE_FRAGMENTED) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_LMAC2_FRAGMENTED; if (iwl_mvm_rrm_scan_needed(mvm) && fw_has_capa(&mvm->fw->ucode_capa, @@ -1497,6 +1568,21 @@ void iwl_mvm_scan_timeout_wk(struct work_struct *work) iwl_force_nmi(mvm->trans); } +static void iwl_mvm_fill_scan_type(struct iwl_mvm *mvm, + struct iwl_mvm_scan_params *params, + bool p2p) +{ + if (iwl_mvm_is_cdb_supported(mvm)) { + params->type = + iwl_mvm_get_scan_type_band(mvm, p2p, + NL80211_BAND_2GHZ); + params->hb_type = + iwl_mvm_get_scan_type_band(mvm, p2p, + NL80211_BAND_5GHZ); + } else { + params->type = iwl_mvm_get_scan_type(mvm, p2p); + } +} int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct cfg80211_scan_request *req, struct ieee80211_scan_ies *ies) @@ -1544,9 +1630,8 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, params.scan_plans = &scan_plan; params.n_scan_plans = 1; - params.type = - iwl_mvm_get_scan_type(mvm, - vif->type == NL80211_IFTYPE_P2P_DEVICE); + iwl_mvm_fill_scan_type(mvm, ¶ms, + vif->type == NL80211_IFTYPE_P2P_DEVICE); ret = iwl_mvm_get_measurement_dwell(mvm, req, ¶ms); if (ret < 0) @@ -1568,6 +1653,8 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (ret) return ret; + iwl_mvm_pause_tcm(mvm, false); + ret = iwl_mvm_send_cmd(mvm, &hcmd); if (ret) { /* If the scan failed, it usually means that the FW was unable @@ -1575,6 +1662,7 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, * should try to send the command again with different params. */ IWL_ERR(mvm, "Scan failed! ret %d\n", ret); + iwl_mvm_resume_tcm(mvm); return ret; } @@ -1638,9 +1726,8 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, params.n_scan_plans = req->n_scan_plans; params.scan_plans = req->scan_plans; - params.type = - iwl_mvm_get_scan_type(mvm, - vif->type == NL80211_IFTYPE_P2P_DEVICE); + iwl_mvm_fill_scan_type(mvm, ¶ms, + vif->type == NL80211_IFTYPE_P2P_DEVICE); /* In theory, LMAC scans can handle a 32-bit delay, but since * waiting for over 18 hours to start the scan is a bit silly @@ -1711,6 +1798,7 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, mvm->scan_vif = NULL; iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); cancel_delayed_work(&mvm->scan_timeout_dwork); + iwl_mvm_resume_tcm(mvm); } else if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_SCHED) { ieee80211_sched_scan_stopped(mvm->hw); mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED; @@ -1827,7 +1915,7 @@ int iwl_mvm_scan_size(struct iwl_mvm *mvm) base_size = IWL_SCAN_REQ_UMAC_SIZE_V8; else if (iwl_mvm_is_adaptive_dwell_supported(mvm)) base_size = IWL_SCAN_REQ_UMAC_SIZE_V7; - else if (iwl_mvm_has_new_tx_api(mvm)) + else if (iwl_mvm_cdb_scan_api(mvm)) base_size = IWL_SCAN_REQ_UMAC_SIZE_V6; if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 80067eb9ea05..9263b9aa8b72 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -35,6 +36,7 @@ * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -214,7 +216,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, cpu_to_le32(agg_size << STA_FLG_MAX_AGG_SIZE_SHIFT); add_sta_cmd.station_flags |= cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT); - if (mvm_sta->associated) + if (mvm_sta->sta_state >= IEEE80211_STA_ASSOC) add_sta_cmd.assoc_id = cpu_to_le16(sta->aid); if (sta->wme) { @@ -2466,6 +2468,15 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, lockdep_assert_held(&mvm->mutex); + if (mvmsta->tid_data[tid].txq_id == IWL_MVM_INVALID_QUEUE && + iwl_mvm_has_new_tx_api(mvm)) { + u8 ac = tid_to_mac80211_ac[tid]; + + ret = iwl_mvm_sta_alloc_queue_tvqm(mvm, sta, ac, tid); + if (ret) + return ret; + } + spin_lock_bh(&mvmsta->lock); /* possible race condition - we entered D0i3 while starting agg */ @@ -2887,7 +2898,7 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, u32 sta_id, struct ieee80211_key_conf *key, bool mcast, u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags, - u8 key_offset) + u8 key_offset, bool mfp) { union { struct iwl_mvm_add_sta_key_cmd_v1 cmd_v1; @@ -2960,6 +2971,8 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, if (mcast) key_flags |= cpu_to_le16(STA_KEY_MULTICAST); + if (mfp) + key_flags |= cpu_to_le16(STA_KEY_MFP); u.cmd.common.key_offset = key_offset; u.cmd.common.key_flags = key_flags; @@ -3101,11 +3114,13 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm, struct ieee80211_key_seq seq; u16 p1k[5]; u32 sta_id; + bool mfp = false; if (sta) { struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); sta_id = mvm_sta->sta_id; + mfp = sta->mfp; } else if (vif->type == NL80211_IFTYPE_AP && !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -3127,7 +3142,8 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm, ieee80211_get_key_rx_seq(keyconf, 0, &seq); ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); ret = iwl_mvm_send_sta_key(mvm, sta_id, keyconf, mcast, - seq.tkip.iv32, p1k, 0, key_offset); + seq.tkip.iv32, p1k, 0, key_offset, + mfp); break; case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_WEP40: @@ -3135,11 +3151,11 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm, case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256: ret = iwl_mvm_send_sta_key(mvm, sta_id, keyconf, mcast, - 0, NULL, 0, key_offset); + 0, NULL, 0, key_offset, mfp); break; default: ret = iwl_mvm_send_sta_key(mvm, sta_id, keyconf, mcast, - 0, NULL, 0, key_offset); + 0, NULL, 0, key_offset, mfp); } return ret; @@ -3366,6 +3382,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, { struct iwl_mvm_sta *mvm_sta; bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); + bool mfp = sta ? sta->mfp : false; rcu_read_lock(); @@ -3373,7 +3390,8 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, if (WARN_ON_ONCE(!mvm_sta)) goto unlock; iwl_mvm_send_sta_key(mvm, mvm_sta->sta_id, keyconf, mcast, - iv32, phase1key, CMD_ASYNC, keyconf->hw_key_idx); + iv32, phase1key, CMD_ASYNC, keyconf->hw_key_idx, + mfp); unlock: rcu_read_unlock(); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 5ffd6adbc383..1c43ea8dd8cc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2015 - 2016 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -18,11 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -35,6 +31,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2015 - 2016 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -376,6 +373,7 @@ struct iwl_mvm_rxq_dup_data { * tid. * @max_agg_bufsize: the maximal size of the AGG buffer for this station * @sta_type: station type + * @sta_state: station state according to enum %ieee80211_sta_state * @bt_reduced_txpower: is reduced tx power enabled for this station * @next_status_eosp: the next reclaimed packet is a PS-Poll response and * we need to signal the EOSP @@ -391,7 +389,9 @@ struct iwl_mvm_rxq_dup_data { * @tx_protection: reference counter for controlling the Tx protection. * @tt_tx_protection: is thermal throttling enable Tx protection? * @disable_tx: is tx to this STA disabled? - * @tlc_amsdu: true if A-MSDU is allowed + * @amsdu_enabled: bitmap of TX AMSDU allowed TIDs. + * In case TLC offload is not active it is either 0xFFFF or 0. + * @max_amsdu_len: max AMSDU length * @agg_tids: bitmap of tids whose status is operational aggregated (IWL_AGG_ON) * @sleep_tx_count: the number of frames that we told the firmware to let out * even when that station is asleep. This is useful in case the queue @@ -414,6 +414,7 @@ struct iwl_mvm_sta { u16 tid_disable_agg; u8 max_agg_bufsize; enum iwl_sta_type sta_type; + enum ieee80211_sta_state sta_state; bool bt_reduced_txpower; bool next_status_eosp; spinlock_t lock; @@ -436,9 +437,9 @@ struct iwl_mvm_sta { bool tt_tx_protection; bool disable_tx; - bool tlc_amsdu; + u16 amsdu_enabled; + u16 max_amsdu_len; bool sleeping; - bool associated; u8 agg_tids; u8 sleep_tx_count; u8 avg_energy; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c index 3d97436bbdf5..67f360c0d17e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c @@ -7,6 +7,7 @@ * * Copyright(c) 2014 Intel Mobile Communications GmbH * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(C) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -18,9 +19,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA + * along with this program. * * The full GNU General Public License is included in this distribution * in the file called COPYING. @@ -33,6 +32,7 @@ * * Copyright(c) 2014 Intel Mobile Communications GmbH * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(C) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -188,8 +188,14 @@ void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (tdls_sta_cnt == 1 && sta_added) iwl_mvm_power_update_mac(mvm); - /* configure the FW with TDLS peer info */ - iwl_mvm_tdls_config(mvm, vif); + /* Configure the FW with TDLS peer info only if TDLS channel switch + * capability is set. + * TDLS config data is used currently only in TDLS channel switch code. + * Supposed to serve also TDLS buffer station which is not implemneted + * yet in FW*/ + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH)) + iwl_mvm_tdls_config(mvm, vif); /* when the last peer leaves, send a power update last */ if (tdls_sta_cnt == 0 && !sta_added) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 795065974d78..cf2591f2ac23 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -767,16 +767,16 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, u16 snap_ip_tcp, pad; unsigned int dbg_max_amsdu_len; netdev_features_t netdev_flags = NETIF_F_CSUM_MASK | NETIF_F_SG; - u8 *qc, tid, txf; + u8 tid, txf; snap_ip_tcp = 8 + skb_transport_header(skb) - skb_network_header(skb) + tcp_hdrlen(skb); dbg_max_amsdu_len = READ_ONCE(mvm->max_amsdu_len); - if (!sta->max_amsdu_len || + if (!mvmsta->max_amsdu_len || !ieee80211_is_data_qos(hdr->frame_control) || - (!mvmsta->tlc_amsdu && !dbg_max_amsdu_len)) + (!mvmsta->amsdu_enabled && !dbg_max_amsdu_len)) return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); /* @@ -790,8 +790,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); } - qc = ieee80211_get_qos_ctl(hdr); - tid = *qc & IEEE80211_QOS_CTL_TID_MASK; + tid = ieee80211_get_tid(hdr); if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) return -EINVAL; @@ -803,7 +802,11 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, !mvmsta->tid_data[tid].amsdu_in_ampdu_allowed) return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); - max_amsdu_len = sta->max_amsdu_len; + if (iwl_mvm_vif_low_latency(iwl_mvm_vif_from_mac80211(mvmsta->vif)) || + !(mvmsta->amsdu_enabled & BIT(tid))) + return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); + + max_amsdu_len = mvmsta->max_amsdu_len; /* the Tx FIFO to which this A-MSDU will be routed */ txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, tid_to_mac80211_ac[tid]); @@ -840,8 +843,10 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, * N * subf_len + (N - 1) * pad. */ num_subframes = (max_amsdu_len + pad) / (subf_len + pad); - if (num_subframes > 1) - *qc |= IEEE80211_QOS_CTL_A_MSDU_PRESENT; + + if (sta->max_amsdu_subframes && + num_subframes > sta->max_amsdu_subframes) + num_subframes = sta->max_amsdu_subframes; tcp_payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) - tcp_hdrlen(skb) + skb->data_len; @@ -852,10 +857,12 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, * 1 more for each fragment * 1 more for the potential data in the header */ - num_subframes = - min_t(unsigned int, num_subframes, - (mvm->trans->max_skb_frags - 1 - - skb_shinfo(skb)->nr_frags) / 2); + if ((num_subframes * 2 + skb_shinfo(skb)->nr_frags + 1) > + mvm->trans->max_skb_frags) + num_subframes = 1; + + if (num_subframes > 1) + *ieee80211_get_qos_ctl(hdr) |= IEEE80211_QOS_CTL_A_MSDU_PRESENT; /* This skb fits in one single A-MSDU */ if (num_subframes * mss >= tcp_payload_len) { @@ -930,6 +937,32 @@ static bool iwl_mvm_txq_should_update(struct iwl_mvm *mvm, int txq_id) return false; } +static void iwl_mvm_tx_airtime(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvmsta, + int airtime) +{ + int mac = mvmsta->mac_id_n_color & FW_CTXT_ID_MSK; + struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[mac]; + + if (mvm->tcm.paused) + return; + + if (time_after(jiffies, mvm->tcm.ts + MVM_TCM_PERIOD)) + schedule_delayed_work(&mvm->tcm.work, 0); + + mdata->tx.airtime += airtime; +} + +static void iwl_mvm_tx_pkt_queued(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvmsta, int tid) +{ + u32 ac = tid_to_mac80211_ac[tid]; + int mac = mvmsta->mac_id_n_color & FW_CTXT_ID_MSK; + struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[mac]; + + mdata->tx.pkts[ac]++; +} + /* * Sets the fields in the Tx cmd that are crypto related */ @@ -976,9 +1009,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, * assignment of MGMT TID */ if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { - u8 *qc = NULL; - qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + tid = ieee80211_get_tid(hdr); if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) goto drop_unlock_sta; @@ -1067,6 +1098,8 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, spin_unlock(&mvmsta->lock); + iwl_mvm_tx_pkt_queued(mvm, mvmsta, tid == IWL_MAX_TID_COUNT ? 0 : tid); + return 0; drop_unlock_sta: @@ -1469,6 +1502,9 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, if (!IS_ERR(sta)) { mvmsta = iwl_mvm_sta_from_mac80211(sta); + iwl_mvm_tx_airtime(mvm, mvmsta, + le16_to_cpu(tx_resp->wireless_media_time)); + if (tid != IWL_TID_NON_QOS && tid != IWL_MGMT_TID) { struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; @@ -1610,6 +1646,8 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, le16_to_cpu(tx_resp->wireless_media_time); mvmsta->tid_data[tid].lq_color = TX_RES_RATE_TABLE_COL_GET(tx_resp->tlc_info); + iwl_mvm_tx_airtime(mvm, mvmsta, + le16_to_cpu(tx_resp->wireless_media_time)); } rcu_read_unlock(); @@ -1800,6 +1838,8 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) le32_to_cpu(ba_res->tx_rate)); } + iwl_mvm_tx_airtime(mvm, mvmsta, + le32_to_cpu(ba_res->wireless_time)); out_unlock: rcu_read_unlock(); out: diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index d99d9ea78e4c..b002a7afb5f5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright (C) 2015 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -35,6 +36,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright (C) 2015 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -278,8 +280,8 @@ u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx) u8 ind = last_idx; int i; - for (i = 0; i < MAX_RS_ANT_NUM; i++) { - ind = (ind + 1) % MAX_RS_ANT_NUM; + for (i = 0; i < MAX_ANT_NUM; i++) { + ind = (ind + 1) % MAX_ANT_NUM; if (valid & BIT(ind)) return ind; } @@ -520,15 +522,15 @@ static void iwl_mvm_dump_lmac_error_log(struct iwl_mvm *mvm, u32 base) /* set INIT_DONE flag */ iwl_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + BIT(trans->cfg->csr->flag_init_done)); /* and wait for clock stabilization */ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) udelay(2); err = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + BIT(trans->cfg->csr->flag_mac_clock_ready), + BIT(trans->cfg->csr->flag_mac_clock_ready), 25000); if (err < 0) { IWL_DEBUG_INFO(trans, @@ -728,12 +730,14 @@ int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, int mac80211_queue, .sta_id = sta_id, .tid = tid, }; - int queue; + int queue, size = IWL_DEFAULT_QUEUE_SIZE; - if (cmd.tid == IWL_MAX_TID_COUNT) + if (cmd.tid == IWL_MAX_TID_COUNT) { cmd.tid = IWL_MGMT_TID; + size = IWL_MGMT_QUEUE_SIZE; + } queue = iwl_trans_txq_alloc(mvm->trans, (void *)&cmd, - SCD_QUEUE_CFG, timeout); + SCD_QUEUE_CFG, size, timeout); if (queue < 0) { IWL_DEBUG_TX_QUEUES(mvm, @@ -1074,23 +1078,48 @@ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return iwl_mvm_power_update_mac(mvm); } +struct iwl_mvm_low_latency_iter { + bool result; + bool result_per_band[NUM_NL80211_BANDS]; +}; + static void iwl_mvm_ll_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) { - bool *result = _data; + struct iwl_mvm_low_latency_iter *result = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + enum nl80211_band band; - if (iwl_mvm_vif_low_latency(iwl_mvm_vif_from_mac80211(vif))) - *result = true; + if (iwl_mvm_vif_low_latency(mvmvif)) { + result->result = true; + + if (!mvmvif->phy_ctxt) + return; + + band = mvmvif->phy_ctxt->channel->band; + result->result_per_band[band] = true; + } } bool iwl_mvm_low_latency(struct iwl_mvm *mvm) { - bool result = false; + struct iwl_mvm_low_latency_iter data = {}; ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_ll_iter, &result); + iwl_mvm_ll_iter, &data); - return result; + return data.result; +} + +bool iwl_mvm_low_latency_band(struct iwl_mvm *mvm, enum nl80211_band band) +{ + struct iwl_mvm_low_latency_iter data = {}; + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_ll_iter, &data); + + return data.result_per_band[band]; } struct iwl_bss_iter_data { @@ -1429,6 +1458,387 @@ void iwl_mvm_event_frame_timeout_callback(struct iwl_mvm *mvm, sta->addr, tid); } +u8 iwl_mvm_tcm_load_percentage(u32 airtime, u32 elapsed) +{ + if (!elapsed) + return 0; + + return (100 * airtime / elapsed) / USEC_PER_MSEC; +} + +static enum iwl_mvm_traffic_load +iwl_mvm_tcm_load(struct iwl_mvm *mvm, u32 airtime, unsigned long elapsed) +{ + u8 load = iwl_mvm_tcm_load_percentage(airtime, elapsed); + + if (load > IWL_MVM_TCM_LOAD_HIGH_THRESH) + return IWL_MVM_TRAFFIC_HIGH; + if (load > IWL_MVM_TCM_LOAD_MEDIUM_THRESH) + return IWL_MVM_TRAFFIC_MEDIUM; + + return IWL_MVM_TRAFFIC_LOW; +} + +struct iwl_mvm_tcm_iter_data { + struct iwl_mvm *mvm; + bool any_sent; +}; + +static void iwl_mvm_tcm_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) +{ + struct iwl_mvm_tcm_iter_data *data = _data; + struct iwl_mvm *mvm = data->mvm; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + bool low_latency, prev = mvmvif->low_latency & LOW_LATENCY_TRAFFIC; + + if (mvmvif->id >= NUM_MAC_INDEX_DRIVER) + return; + + low_latency = mvm->tcm.result.low_latency[mvmvif->id]; + + if (!mvm->tcm.result.change[mvmvif->id] && + prev == low_latency) { + iwl_mvm_update_quotas(mvm, false, NULL); + return; + } + + if (prev != low_latency) { + /* this sends traffic load and updates quota as well */ + iwl_mvm_update_low_latency(mvm, vif, low_latency, + LOW_LATENCY_TRAFFIC); + } else { + iwl_mvm_update_quotas(mvm, false, NULL); + } + + data->any_sent = true; +} + +static void iwl_mvm_tcm_results(struct iwl_mvm *mvm) +{ + struct iwl_mvm_tcm_iter_data data = { + .mvm = mvm, + .any_sent = false, + }; + + mutex_lock(&mvm->mutex); + + ieee80211_iterate_active_interfaces( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_tcm_iter, &data); + + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) + iwl_mvm_config_scan(mvm); + + mutex_unlock(&mvm->mutex); +} + +static void iwl_mvm_tcm_uapsd_nonagg_detected_wk(struct work_struct *wk) +{ + struct iwl_mvm *mvm; + struct iwl_mvm_vif *mvmvif; + struct ieee80211_vif *vif; + + mvmvif = container_of(wk, struct iwl_mvm_vif, + uapsd_nonagg_detected_wk.work); + vif = container_of((void *)mvmvif, struct ieee80211_vif, drv_priv); + mvm = mvmvif->mvm; + + if (mvm->tcm.data[mvmvif->id].opened_rx_ba_sessions) + return; + + /* remember that this AP is broken */ + memcpy(mvm->uapsd_noagg_bssids[mvm->uapsd_noagg_bssid_write_idx].addr, + vif->bss_conf.bssid, ETH_ALEN); + mvm->uapsd_noagg_bssid_write_idx++; + if (mvm->uapsd_noagg_bssid_write_idx >= IWL_MVM_UAPSD_NOAGG_LIST_LEN) + mvm->uapsd_noagg_bssid_write_idx = 0; + + iwl_mvm_connection_loss(mvm, vif, + "AP isn't using AMPDU with uAPSD enabled"); +} + +static void iwl_mvm_uapsd_agg_disconnect_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + int *mac_id = data; + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (mvmvif->id != *mac_id) + return; + + if (!vif->bss_conf.assoc) + return; + + if (!mvmvif->queue_params[IEEE80211_AC_VO].uapsd && + !mvmvif->queue_params[IEEE80211_AC_VI].uapsd && + !mvmvif->queue_params[IEEE80211_AC_BE].uapsd && + !mvmvif->queue_params[IEEE80211_AC_BK].uapsd) + return; + + if (mvm->tcm.data[*mac_id].uapsd_nonagg_detect.detected) + return; + + mvm->tcm.data[*mac_id].uapsd_nonagg_detect.detected = true; + IWL_INFO(mvm, + "detected AP should do aggregation but isn't, likely due to U-APSD\n"); + schedule_delayed_work(&mvmvif->uapsd_nonagg_detected_wk, 15 * HZ); +} + +static void iwl_mvm_check_uapsd_agg_expected_tpt(struct iwl_mvm *mvm, + unsigned int elapsed, + int mac) +{ + u64 bytes = mvm->tcm.data[mac].uapsd_nonagg_detect.rx_bytes; + u64 tpt; + unsigned long rate; + + rate = ewma_rate_read(&mvm->tcm.data[mac].uapsd_nonagg_detect.rate); + + if (!rate || mvm->tcm.data[mac].opened_rx_ba_sessions || + mvm->tcm.data[mac].uapsd_nonagg_detect.detected) + return; + + if (iwl_mvm_has_new_rx_api(mvm)) { + tpt = 8 * bytes; /* kbps */ + do_div(tpt, elapsed); + rate *= 1000; /* kbps */ + if (tpt < 22 * rate / 100) + return; + } else { + /* + * the rate here is actually the threshold, in 100Kbps units, + * so do the needed conversion from bytes to 100Kbps: + * 100kb = bits / (100 * 1000), + * 100kbps = 100kb / (msecs / 1000) == + * (bits / (100 * 1000)) / (msecs / 1000) == + * bits / (100 * msecs) + */ + tpt = (8 * bytes); + do_div(tpt, elapsed * 100); + if (tpt < rate) + return; + } + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_uapsd_agg_disconnect_iter, &mac); +} + +static void iwl_mvm_tcm_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + u32 *band = _data; + + if (!mvmvif->phy_ctxt) + return; + + band[mvmvif->id] = mvmvif->phy_ctxt->channel->band; +} + +static unsigned long iwl_mvm_calc_tcm_stats(struct iwl_mvm *mvm, + unsigned long ts, + bool handle_uapsd) +{ + unsigned int elapsed = jiffies_to_msecs(ts - mvm->tcm.ts); + unsigned int uapsd_elapsed = + jiffies_to_msecs(ts - mvm->tcm.uapsd_nonagg_ts); + u32 total_airtime = 0; + u32 band_airtime[NUM_NL80211_BANDS] = {0}; + u32 band[NUM_MAC_INDEX_DRIVER] = {0}; + int ac, mac, i; + bool low_latency = false; + enum iwl_mvm_traffic_load load, band_load; + bool handle_ll = time_after(ts, mvm->tcm.ll_ts + MVM_LL_PERIOD); + + if (handle_ll) + mvm->tcm.ll_ts = ts; + if (handle_uapsd) + mvm->tcm.uapsd_nonagg_ts = ts; + + mvm->tcm.result.elapsed = elapsed; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_tcm_iterator, + &band); + + for (mac = 0; mac < NUM_MAC_INDEX_DRIVER; mac++) { + struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[mac]; + u32 vo_vi_pkts = 0; + u32 airtime = mdata->rx.airtime + mdata->tx.airtime; + + total_airtime += airtime; + band_airtime[band[mac]] += airtime; + + load = iwl_mvm_tcm_load(mvm, airtime, elapsed); + mvm->tcm.result.change[mac] = load != mvm->tcm.result.load[mac]; + mvm->tcm.result.load[mac] = load; + mvm->tcm.result.airtime[mac] = airtime; + + for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_VI; ac++) + vo_vi_pkts += mdata->rx.pkts[ac] + + mdata->tx.pkts[ac]; + + /* enable immediately with enough packets but defer disabling */ + if (vo_vi_pkts > IWL_MVM_TCM_LOWLAT_ENABLE_THRESH) + mvm->tcm.result.low_latency[mac] = true; + else if (handle_ll) + mvm->tcm.result.low_latency[mac] = false; + + if (handle_ll) { + /* clear old data */ + memset(&mdata->rx.pkts, 0, sizeof(mdata->rx.pkts)); + memset(&mdata->tx.pkts, 0, sizeof(mdata->tx.pkts)); + } + low_latency |= mvm->tcm.result.low_latency[mac]; + + if (!mvm->tcm.result.low_latency[mac] && handle_uapsd) + iwl_mvm_check_uapsd_agg_expected_tpt(mvm, uapsd_elapsed, + mac); + /* clear old data */ + if (handle_uapsd) + mdata->uapsd_nonagg_detect.rx_bytes = 0; + memset(&mdata->rx.airtime, 0, sizeof(mdata->rx.airtime)); + memset(&mdata->tx.airtime, 0, sizeof(mdata->tx.airtime)); + } + + load = iwl_mvm_tcm_load(mvm, total_airtime, elapsed); + mvm->tcm.result.global_change = load != mvm->tcm.result.global_load; + mvm->tcm.result.global_load = load; + + for (i = 0; i < NUM_NL80211_BANDS; i++) { + band_load = iwl_mvm_tcm_load(mvm, band_airtime[i], elapsed); + mvm->tcm.result.band_load[i] = band_load; + } + + /* + * If the current load isn't low we need to force re-evaluation + * in the TCM period, so that we can return to low load if there + * was no traffic at all (and thus iwl_mvm_recalc_tcm didn't get + * triggered by traffic). + */ + if (load != IWL_MVM_TRAFFIC_LOW) + return MVM_TCM_PERIOD; + /* + * If low-latency is active we need to force re-evaluation after + * (the longer) MVM_LL_PERIOD, so that we can disable low-latency + * when there's no traffic at all. + */ + if (low_latency) + return MVM_LL_PERIOD; + /* + * Otherwise, we don't need to run the work struct because we're + * in the default "idle" state - traffic indication is low (which + * also covers the "no traffic" case) and low-latency is disabled + * so there's no state that may need to be disabled when there's + * no traffic at all. + * + * Note that this has no impact on the regular scheduling of the + * updates triggered by traffic - those happen whenever one of the + * two timeouts expire (if there's traffic at all.) + */ + return 0; +} + +void iwl_mvm_recalc_tcm(struct iwl_mvm *mvm) +{ + unsigned long ts = jiffies; + bool handle_uapsd = + time_after(ts, mvm->tcm.uapsd_nonagg_ts + + msecs_to_jiffies(IWL_MVM_UAPSD_NONAGG_PERIOD)); + + spin_lock(&mvm->tcm.lock); + if (mvm->tcm.paused || !time_after(ts, mvm->tcm.ts + MVM_TCM_PERIOD)) { + spin_unlock(&mvm->tcm.lock); + return; + } + spin_unlock(&mvm->tcm.lock); + + if (handle_uapsd && iwl_mvm_has_new_rx_api(mvm)) { + mutex_lock(&mvm->mutex); + if (iwl_mvm_request_statistics(mvm, true)) + handle_uapsd = false; + mutex_unlock(&mvm->mutex); + } + + spin_lock(&mvm->tcm.lock); + /* re-check if somebody else won the recheck race */ + if (!mvm->tcm.paused && time_after(ts, mvm->tcm.ts + MVM_TCM_PERIOD)) { + /* calculate statistics */ + unsigned long work_delay = iwl_mvm_calc_tcm_stats(mvm, ts, + handle_uapsd); + + /* the memset needs to be visible before the timestamp */ + smp_mb(); + mvm->tcm.ts = ts; + if (work_delay) + schedule_delayed_work(&mvm->tcm.work, work_delay); + } + spin_unlock(&mvm->tcm.lock); + + iwl_mvm_tcm_results(mvm); +} + +void iwl_mvm_tcm_work(struct work_struct *work) +{ + struct delayed_work *delayed_work = to_delayed_work(work); + struct iwl_mvm *mvm = container_of(delayed_work, struct iwl_mvm, + tcm.work); + + iwl_mvm_recalc_tcm(mvm); +} + +void iwl_mvm_pause_tcm(struct iwl_mvm *mvm, bool with_cancel) +{ + spin_lock_bh(&mvm->tcm.lock); + mvm->tcm.paused = true; + spin_unlock_bh(&mvm->tcm.lock); + if (with_cancel) + cancel_delayed_work_sync(&mvm->tcm.work); +} + +void iwl_mvm_resume_tcm(struct iwl_mvm *mvm) +{ + int mac; + + spin_lock_bh(&mvm->tcm.lock); + mvm->tcm.ts = jiffies; + mvm->tcm.ll_ts = jiffies; + for (mac = 0; mac < NUM_MAC_INDEX_DRIVER; mac++) { + struct iwl_mvm_tcm_mac *mdata = &mvm->tcm.data[mac]; + + memset(&mdata->rx.pkts, 0, sizeof(mdata->rx.pkts)); + memset(&mdata->tx.pkts, 0, sizeof(mdata->tx.pkts)); + memset(&mdata->rx.airtime, 0, sizeof(mdata->rx.airtime)); + memset(&mdata->tx.airtime, 0, sizeof(mdata->tx.airtime)); + } + /* The TCM data needs to be reset before "paused" flag changes */ + smp_mb(); + mvm->tcm.paused = false; + spin_unlock_bh(&mvm->tcm.lock); +} + +void iwl_mvm_tcm_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + INIT_DELAYED_WORK(&mvmvif->uapsd_nonagg_detected_wk, + iwl_mvm_tcm_uapsd_nonagg_detected_wk); +} + +void iwl_mvm_tcm_rm_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + cancel_delayed_work_sync(&mvmvif->uapsd_nonagg_detected_wk); +} + + void iwl_mvm_get_sync_time(struct iwl_mvm *mvm, u32 *gp2, u64 *boottime) { bool ps_disabled; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c index 5ef216f3a60b..3fc4343581ee 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c @@ -244,7 +244,7 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, ctxt_info->hcmd_cfg.cmd_queue_addr = cpu_to_le64(trans_pcie->txq[trans_pcie->cmd_queue]->dma_addr); ctxt_info->hcmd_cfg.cmd_queue_size = - TFD_QUEUE_CB_SIZE(trans_pcie->tx_cmd_queue_size); + TFD_QUEUE_CB_SIZE(TFD_CMD_SLOTS); /* allocate ucode sections in dram and set addresses */ ret = iwl_pcie_ctxt_info_init_fw_sec(trans, fw, ctxt_info); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 959de2f8bb28..38234bda9017 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -836,6 +836,9 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct iwl_trans *iwl_trans; int ret; + if (WARN_ONCE(!cfg->csr, "CSR addresses aren't configured\n")) + return -EINVAL; + iwl_trans = iwl_trans_pcie_alloc(pdev, ent, cfg); if (IS_ERR(iwl_trans)) return PTR_ERR(iwl_trans); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index ca3b64ff4dad..45ea32796cda 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -383,7 +383,8 @@ struct iwl_self_init_dram { * @hw_init_mask: initial unmasked hw causes * @fh_mask: current unmasked fh causes * @hw_mask: current unmasked hw causes - * @tx_cmd_queue_size: the size of the tx command queue + * @in_rescan: true if we have triggered a device rescan + * @scheduled_for_removal: true if we have scheduled a device removal */ struct iwl_trans_pcie { struct iwl_rxq *rxq; @@ -466,6 +467,8 @@ struct iwl_trans_pcie { u32 hw_mask; cpumask_t affinity_mask[IWL_MAX_RX_HW_QUEUES]; u16 tx_cmd_queue_size; + bool in_rescan; + bool scheduled_for_removal; }; static inline struct iwl_trans_pcie * @@ -537,7 +540,6 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, struct sk_buff_head *skbs); void iwl_trans_pcie_tx_reset(struct iwl_trans *trans); -void iwl_pcie_set_tx_cmd_queue_size(struct iwl_trans *trans); static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_trans *trans, void *_tfd, u8 idx) @@ -822,7 +824,7 @@ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans, u32 scd_addr); int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, struct iwl_tx_queue_cfg_cmd *cmd, - int cmd_id, + int cmd_id, int size, unsigned int timeout); void iwl_trans_pcie_dyn_txq_free(struct iwl_trans *trans, int queue); int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index f25ce3a1ea50..d15f5ba2dc77 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -3,6 +3,7 @@ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -201,7 +202,7 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans, IWL_DEBUG_INFO(trans, "Rx queue requesting wakeup, GP1 = 0x%x\n", reg); iwl_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + BIT(trans->cfg->csr->flag_mac_access_req)); rxq->need_update = true; return; } @@ -901,6 +902,8 @@ static int _iwl_pcie_rx_init(struct iwl_trans *trans) } def_rxq = trans_pcie->rxq; + cancel_work_sync(&rba->rx_alloc); + spin_lock(&rba->lock); atomic_set(&rba->req_pending, 0); atomic_set(&rba->req_ready, 0); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index cb4012541f45..b8e8dac2895d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -19,6 +20,7 @@ * BSD LICENSE * * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -92,7 +94,8 @@ static int iwl_pcie_gen2_apm_init(struct iwl_trans *trans) * Set "initialization complete" bit to move adapter from * D0U* --> D0A* (powered-up active) state. */ - iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + iwl_set_bit(trans, CSR_GP_CNTRL, + BIT(trans->cfg->csr->flag_init_done)); /* * Wait for clock stabilization; once stabilized, access to @@ -100,8 +103,9 @@ static int iwl_pcie_gen2_apm_init(struct iwl_trans *trans) * and accesses to uCode SRAM. */ ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); + BIT(trans->cfg->csr->flag_mac_clock_ready), + BIT(trans->cfg->csr->flag_mac_clock_ready), + 25000); if (ret < 0) { IWL_DEBUG_INFO(trans, "Failed to init the card\n"); return ret; @@ -143,7 +147,8 @@ static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave) * Clear "initialization complete" bit to move adapter from * D0A* (powered-up Active) --> D0U* (Uninitialized) state. */ - iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + iwl_clear_bit(trans, CSR_GP_CNTRL, + BIT(trans->cfg->csr->flag_init_done)); } void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans, bool low_power) @@ -187,7 +192,7 @@ void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans, bool low_power) /* Make sure (redundant) we've released our request to stay awake */ iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + BIT(trans->cfg->csr->flag_mac_access_req)); /* Stop the device, and put it in low power state */ iwl_pcie_gen2_apm_stop(trans, false); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 5517ea4c2aa0..7229991ae70d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -8,6 +8,7 @@ * Copyright(c) 2007 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -35,6 +36,7 @@ * Copyright(c) 2005 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -73,6 +75,7 @@ #include <linux/gfp.h> #include <linux/vmalloc.h> #include <linux/pm_runtime.h> +#include <linux/module.h> #include "iwl-drv.h" #include "iwl-trans.h" @@ -179,7 +182,8 @@ out: static void iwl_trans_pcie_sw_reset(struct iwl_trans *trans) { /* Reset entire device - do controller reset (results in SHRD_HW_RST) */ - iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + iwl_set_bit(trans, trans->cfg->csr->addr_sw_reset, + BIT(trans->cfg->csr->flag_sw_reset)); usleep_range(5000, 6000); } @@ -372,7 +376,8 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans) * Set "initialization complete" bit to move adapter from * D0U* --> D0A* (powered-up active) state. */ - iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + iwl_set_bit(trans, CSR_GP_CNTRL, + BIT(trans->cfg->csr->flag_init_done)); /* * Wait for clock stabilization; once stabilized, access to @@ -380,8 +385,9 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans) * and accesses to uCode SRAM. */ ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); + BIT(trans->cfg->csr->flag_mac_clock_ready), + BIT(trans->cfg->csr->flag_mac_clock_ready), + 25000); if (ret < 0) { IWL_ERR(trans, "Failed to init the card\n"); return ret; @@ -459,15 +465,16 @@ static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans) * Set "initialization complete" bit to move adapter from * D0U* --> D0A* (powered-up active) state. */ - iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + iwl_set_bit(trans, CSR_GP_CNTRL, + BIT(trans->cfg->csr->flag_init_done)); /* * Wait for clock stabilization; once stabilized, access to * device-internal resources is possible. */ ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + BIT(trans->cfg->csr->flag_mac_clock_ready), + BIT(trans->cfg->csr->flag_mac_clock_ready), 25000); if (WARN_ON(ret < 0)) { IWL_ERR(trans, "Access time out - failed to enable LP XTAL\n"); @@ -519,7 +526,7 @@ static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans) * D0A* (powered-up Active) --> D0U* (Uninitialized) state. */ iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + BIT(trans->cfg->csr->flag_init_done)); /* Activates XTAL resources monitor */ __iwl_trans_pcie_set_bit(trans, CSR_MONITOR_CFG_REG, @@ -541,11 +548,12 @@ void iwl_pcie_apm_stop_master(struct iwl_trans *trans) int ret; /* stop device's busmaster DMA activity */ - iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); + iwl_set_bit(trans, trans->cfg->csr->addr_sw_reset, + BIT(trans->cfg->csr->flag_stop_master)); - ret = iwl_poll_bit(trans, CSR_RESET, - CSR_RESET_REG_FLAG_MASTER_DISABLED, - CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); + ret = iwl_poll_bit(trans, trans->cfg->csr->addr_sw_reset, + BIT(trans->cfg->csr->flag_master_dis), + BIT(trans->cfg->csr->flag_master_dis), 100); if (ret < 0) IWL_WARN(trans, "Master Disable Timed Out, 100 usec\n"); @@ -594,7 +602,7 @@ static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave) * D0A* (powered-up Active) --> D0U* (Uninitialized) state. */ iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + BIT(trans->cfg->csr->flag_init_done)); } static int iwl_pcie_nic_init(struct iwl_trans *trans) @@ -1267,7 +1275,7 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) /* Make sure (redundant) we've released our request to stay awake */ iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + BIT(trans->cfg->csr->flag_mac_access_req)); /* Stop the device, and put it in low power state */ iwl_pcie_apm_stop(trans, false); @@ -1497,9 +1505,9 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test, iwl_pcie_synchronize_irqs(trans); iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + BIT(trans->cfg->csr->flag_mac_access_req)); iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + BIT(trans->cfg->csr->flag_init_done)); iwl_pcie_enable_rx_wake(trans, false); @@ -1543,15 +1551,17 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, iwl_pcie_reset_ict(trans); iwl_enable_interrupts(trans); - iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + iwl_set_bit(trans, CSR_GP_CNTRL, + BIT(trans->cfg->csr->flag_mac_access_req)); + iwl_set_bit(trans, CSR_GP_CNTRL, + BIT(trans->cfg->csr->flag_init_done)); if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000) udelay(2); ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + BIT(trans->cfg->csr->flag_mac_clock_ready), + BIT(trans->cfg->csr->flag_mac_clock_ready), 25000); if (ret < 0) { IWL_ERR(trans, "Failed to resume the device (mac ready)\n"); @@ -1562,7 +1572,7 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, if (!reset) { iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + BIT(trans->cfg->csr->flag_mac_access_req)); } else { iwl_trans_pcie_tx_reset(trans); @@ -1926,6 +1936,29 @@ static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state) clear_bit(STATUS_TPOWER_PMI, &trans->status); } +struct iwl_trans_pcie_removal { + struct pci_dev *pdev; + struct work_struct work; +}; + +static void iwl_trans_pcie_removal_wk(struct work_struct *wk) +{ + struct iwl_trans_pcie_removal *removal = + container_of(wk, struct iwl_trans_pcie_removal, work); + struct pci_dev *pdev = removal->pdev; + char *prop[] = {"EVENT=INACCESSIBLE", NULL}; + + dev_err(&pdev->dev, "Device gone - attempting removal\n"); + kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, prop); + pci_lock_rescan_remove(); + pci_dev_put(pdev); + pci_stop_and_remove_bus_device(pdev); + pci_unlock_rescan_remove(); + + kfree(removal); + module_put(THIS_MODULE); +} + static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, unsigned long *flags) { @@ -1939,7 +1972,7 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, /* this bit wakes up the NIC */ __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + BIT(trans->cfg->csr->flag_mac_access_req)); if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000) udelay(2); @@ -1964,15 +1997,59 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, * and do not save/restore SRAM when power cycling. */ ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, - (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | + BIT(trans->cfg->csr->flag_val_mac_access_en), + (BIT(trans->cfg->csr->flag_mac_clock_ready) | CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000); if (unlikely(ret < 0)) { - iwl_trans_pcie_dump_regs(trans); - iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI); + u32 cntrl = iwl_read32(trans, CSR_GP_CNTRL); + WARN_ONCE(1, "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n", - iwl_read32(trans, CSR_GP_CNTRL)); + cntrl); + + iwl_trans_pcie_dump_regs(trans); + + if (iwlwifi_mod_params.remove_when_gone && cntrl == ~0U) { + struct iwl_trans_pcie_removal *removal; + + if (trans_pcie->scheduled_for_removal) + goto err; + + IWL_ERR(trans, "Device gone - scheduling removal!\n"); + + /* + * get a module reference to avoid doing this + * while unloading anyway and to avoid + * scheduling a work with code that's being + * removed. + */ + if (!try_module_get(THIS_MODULE)) { + IWL_ERR(trans, + "Module is being unloaded - abort\n"); + goto err; + } + + removal = kzalloc(sizeof(*removal), GFP_ATOMIC); + if (!removal) { + module_put(THIS_MODULE); + goto err; + } + /* + * we don't need to clear this flag, because + * the trans will be freed and reallocated. + */ + trans_pcie->scheduled_for_removal = true; + + removal->pdev = to_pci_dev(trans->dev); + INIT_WORK(&removal->work, iwl_trans_pcie_removal_wk); + pci_dev_get(removal->pdev); + schedule_work(&removal->work); + } else { + iwl_write32(trans, CSR_RESET, + CSR_RESET_REG_FLAG_FORCE_NMI); + } + +err: spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags); return false; } @@ -2003,7 +2080,7 @@ static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans, goto out; __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + BIT(trans->cfg->csr->flag_mac_access_req)); /* * Above we read the CSR_GP_CNTRL register, which will flush * any previous writes, but we need the write that clears the @@ -3232,12 +3309,12 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, * id located at the AUX bus MISC address space. */ iwl_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + BIT(trans->cfg->csr->flag_init_done)); udelay(2); ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + BIT(trans->cfg->csr->flag_mac_clock_ready), + BIT(trans->cfg->csr->flag_mac_clock_ready), 25000); if (ret < 0) { IWL_DEBUG_INFO(trans, "Failed to wake up the nic\n"); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index fabae0f60683..48890a1c825f 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -488,6 +488,23 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, spin_lock(&txq->lock); + if (iwl_queue_space(txq) < txq->high_mark) { + iwl_stop_queue(trans, txq); + + /* don't put the packet on the ring, if there is no room */ + if (unlikely(iwl_queue_space(txq) < 3)) { + struct iwl_device_cmd **dev_cmd_ptr; + + dev_cmd_ptr = (void *)((u8 *)skb->cb + + trans_pcie->dev_cmd_offs); + + *dev_cmd_ptr = dev_cmd; + __skb_queue_tail(&txq->overflow_q, skb); + spin_unlock(&txq->lock); + return 0; + } + } + idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr); /* Set up driver data for this TFD */ @@ -523,9 +540,6 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, /* Tell device the write index *just past* this latest filled TFD */ txq->write_ptr = iwl_queue_inc_wrap(txq->write_ptr); iwl_pcie_gen2_txq_inc_wr_ptr(trans, txq); - if (iwl_queue_space(txq) < txq->high_mark) - iwl_stop_queue(trans, txq); - /* * At this point the frame is "transmitted" successfully * and we will get a TX status notification eventually. @@ -555,15 +569,13 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, unsigned long flags; void *dup_buf = NULL; dma_addr_t phys_addr; - int i, cmd_pos, idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr); + int i, cmd_pos, idx; u16 copy_size, cmd_size, tb0_size; bool had_nocopy = false; u8 group_id = iwl_cmd_groupid(cmd->id); const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD]; u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD]; - struct iwl_tfh_tfd *tfd = iwl_pcie_get_tfd(trans, txq, txq->write_ptr); - - memset(tfd, 0, sizeof(*tfd)); + struct iwl_tfh_tfd *tfd; copy_size = sizeof(struct iwl_cmd_header_wide); cmd_size = sizeof(struct iwl_cmd_header_wide); @@ -634,6 +646,10 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, spin_lock_bh(&txq->lock); + idx = iwl_pcie_get_cmd_index(txq, txq->write_ptr); + tfd = iwl_pcie_get_tfd(trans, txq, txq->write_ptr); + memset(tfd, 0, sizeof(*tfd)); + if (iwl_queue_space(txq) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { spin_unlock_bh(&txq->lock); @@ -957,6 +973,13 @@ void iwl_pcie_gen2_txq_unmap(struct iwl_trans *trans, int txq_id) spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); } } + + while (!skb_queue_empty(&txq->overflow_q)) { + struct sk_buff *skb = __skb_dequeue(&txq->overflow_q); + + iwl_op_mode_free_skb(trans->op_mode, skb); + } + spin_unlock_bh(&txq->lock); /* just in case - this queue may have been stopped */ @@ -972,7 +995,7 @@ static void iwl_pcie_gen2_txq_free_memory(struct iwl_trans *trans, /* De-alloc circular buffer of TFDs */ if (txq->tfds) { dma_free_coherent(dev, - trans_pcie->tfd_size * TFD_QUEUE_SIZE_MAX, + trans_pcie->tfd_size * txq->n_window, txq->tfds, txq->dma_addr); dma_free_coherent(dev, sizeof(*txq->first_tb_bufs) * txq->n_window, @@ -1020,7 +1043,7 @@ static void iwl_pcie_gen2_txq_free(struct iwl_trans *trans, int txq_id) int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, struct iwl_tx_queue_cfg_cmd *cmd, - int cmd_id, + int cmd_id, int size, unsigned int timeout) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -1046,12 +1069,12 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, return -ENOMEM; } - ret = iwl_pcie_txq_alloc(trans, txq, TFD_TX_CMD_SLOTS, false); + ret = iwl_pcie_txq_alloc(trans, txq, size, false); if (ret) { IWL_ERR(trans, "Tx queue alloc failed\n"); goto error; } - ret = iwl_pcie_txq_init(trans, txq, TFD_TX_CMD_SLOTS, false); + ret = iwl_pcie_txq_init(trans, txq, size, false); if (ret) { IWL_ERR(trans, "Tx queue init failed\n"); goto error; @@ -1061,7 +1084,7 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, cmd->tfdq_addr = cpu_to_le64(txq->dma_addr); cmd->byte_cnt_addr = cpu_to_le64(txq->bc_tbl.dma); - cmd->cb_size = cpu_to_le32(TFD_QUEUE_CB_SIZE(TFD_TX_CMD_SLOTS)); + cmd->cb_size = cpu_to_le32(TFD_QUEUE_CB_SIZE(size)); ret = iwl_trans_send_cmd(trans, &hcmd); if (ret) @@ -1152,8 +1175,6 @@ int iwl_pcie_gen2_tx_init(struct iwl_trans *trans) struct iwl_txq *cmd_queue; int txq_id = trans_pcie->cmd_queue, ret; - iwl_pcie_set_tx_cmd_queue_size(trans); - /* alloc and init the command queue */ if (!trans_pcie->txq[txq_id]) { cmd_queue = kzalloc(sizeof(*cmd_queue), GFP_KERNEL); @@ -1162,8 +1183,7 @@ int iwl_pcie_gen2_tx_init(struct iwl_trans *trans) return -ENOMEM; } trans_pcie->txq[txq_id] = cmd_queue; - ret = iwl_pcie_txq_alloc(trans, cmd_queue, - trans_pcie->tx_cmd_queue_size, true); + ret = iwl_pcie_txq_alloc(trans, cmd_queue, TFD_CMD_SLOTS, true); if (ret) { IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); goto error; @@ -1172,8 +1192,7 @@ int iwl_pcie_gen2_tx_init(struct iwl_trans *trans) cmd_queue = trans_pcie->txq[txq_id]; } - ret = iwl_pcie_txq_init(trans, cmd_queue, - trans_pcie->tx_cmd_queue_size, true); + ret = iwl_pcie_txq_init(trans, cmd_queue, TFD_CMD_SLOTS, true); if (ret) { IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); goto error; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 1a566287993d..473fe7ccb07c 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -3,6 +3,7 @@ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -273,7 +274,7 @@ static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, IWL_DEBUG_INFO(trans, "Tx queue %d requesting wakeup, GP1 = 0x%x\n", txq_id, reg); iwl_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + BIT(trans->cfg->csr->flag_mac_access_req)); txq->need_update = true; return; } @@ -495,6 +496,9 @@ int iwl_pcie_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq, if (WARN_ON(txq->entries || txq->tfds)) return -EINVAL; + if (trans->cfg->use_tfh) + tfd_sz = trans_pcie->tfd_size * slots_num; + timer_setup(&txq->stuck_timer, iwl_pcie_txq_stuck_timer, 0); txq->trans_pcie = trans_pcie; @@ -608,7 +612,7 @@ static void iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans) trans_pcie->cmd_hold_nic_awake = false; __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + BIT(trans->cfg->csr->flag_mac_access_req)); } /* @@ -950,8 +954,7 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans) txq_id++) { bool cmd_queue = (txq_id == trans_pcie->cmd_queue); - slots_num = cmd_queue ? trans_pcie->tx_cmd_queue_size : - TFD_TX_CMD_SLOTS; + slots_num = cmd_queue ? TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; trans_pcie->txq[txq_id] = &trans_pcie->txq_memory[txq_id]; ret = iwl_pcie_txq_alloc(trans, trans_pcie->txq[txq_id], slots_num, cmd_queue); @@ -970,21 +973,6 @@ error: return ret; } -void iwl_pcie_set_tx_cmd_queue_size(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int queue_size = TFD_CMD_SLOTS; - - if (trans->cfg->tx_cmd_queue_size) - queue_size = trans->cfg->tx_cmd_queue_size; - - if (WARN_ON(!(is_power_of_2(queue_size) && - TFD_QUEUE_CB_SIZE(queue_size) > 0))) - trans_pcie->tx_cmd_queue_size = TFD_CMD_SLOTS; - else - trans_pcie->tx_cmd_queue_size = queue_size; -} - int iwl_pcie_tx_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -992,8 +980,6 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) int txq_id, slots_num; bool alloc = false; - iwl_pcie_set_tx_cmd_queue_size(trans); - if (!trans_pcie->txq_memory) { ret = iwl_pcie_tx_alloc(trans); if (ret) @@ -1017,8 +1003,7 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) txq_id++) { bool cmd_queue = (txq_id == trans_pcie->cmd_queue); - slots_num = cmd_queue ? trans_pcie->tx_cmd_queue_size : - TFD_TX_CMD_SLOTS; + slots_num = cmd_queue ? TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; ret = iwl_pcie_txq_init(trans, trans_pcie->txq[txq_id], slots_num, cmd_queue); if (ret) { @@ -1166,7 +1151,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, * In that case, iwl_queue_space will be small again * and we won't wake mac80211's queue. */ - iwl_trans_pcie_tx(trans, skb, dev_cmd_ptr, txq_id); + iwl_trans_tx(trans, skb, dev_cmd_ptr, txq_id); } spin_lock_bh(&txq->lock); @@ -1187,6 +1172,7 @@ static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans, const struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + const struct iwl_cfg *cfg = trans->cfg; int ret; lockdep_assert_held(&trans_pcie->reg_lock); @@ -1204,19 +1190,19 @@ static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans, * returned. This needs to be done only on NICs that have * apmg_wake_up_wa set. */ - if (trans->cfg->base_params->apmg_wake_up_wa && + if (cfg->base_params->apmg_wake_up_wa && !trans_pcie->cmd_hold_nic_awake) { __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + BIT(cfg->csr->flag_mac_access_req)); ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, - (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | + BIT(cfg->csr->flag_val_mac_access_en), + (BIT(cfg->csr->flag_mac_clock_ready) | CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000); if (ret < 0) { __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + BIT(cfg->csr->flag_mac_access_req)); IWL_ERR(trans, "Failed to wake NIC for hcmd\n"); return -EIO; } @@ -2411,7 +2397,13 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, goto out_err; iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false); - if (amsdu) { + /* + * If gso_size wasn't set, don't give the frame "amsdu treatment" + * (adding subframes, etc.). + * This can happen in some testing flows when the amsdu was already + * pre-built, and we just need to send the resulting skb. + */ + if (amsdu && skb_shinfo(skb)->gso_size) { if (unlikely(iwl_fill_data_tbs_amsdu(trans, skb, txq, hdr_len, out_meta, dev_cmd, tb1_len))) |