From 7b08c67fa5ae66c2d07fd506de57de67b7a7e5d6 Mon Sep 17 00:00:00 2001 From: Oren Givon Date: Thu, 21 Jan 2016 11:18:34 +0200 Subject: [PATCH 1/9] iwlwifi: add new 3168 series devices support Add new sub-system PCI IDs to the 3168 series. Added 0x2010, 0x2050 and 0x2150 sub-system IDs. Signed-off-by: Oren Givon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 6261a68cae90..944e88ab13a8 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -378,7 +378,10 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x3165, 0x8110, iwl3165_2ac_cfg)}, /* 3168 Series */ + {IWL_PCI_DEVICE(0x24FB, 0x2010, iwl3168_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FB, 0x2110, iwl3168_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FB, 0x2050, iwl3168_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FB, 0x2150, iwl3168_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FB, 0x0000, iwl3168_2ac_cfg)}, /* 7265 Series */ From 9baa03982b06a62ac817b4983f2da7ca66d4f331 Mon Sep 17 00:00:00 2001 From: Oren Givon Date: Thu, 21 Jan 2016 11:11:11 +0200 Subject: [PATCH 2/9] iwlwifi: add device ID for 8265 Signed-off-by: Oren Givon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 944e88ab13a8..00335ea6b3eb 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -478,6 +478,7 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x24F3, 0x0000, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x0010, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x8010, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0810, iwl8265_2ac_cfg)}, /* 9000 Series */ {IWL_PCI_DEVICE(0x9DF0, 0x2A10, iwl5165_2ac_cfg)}, From ca296c571f6929e42dc0ac0bb5c81cdb5323362d Mon Sep 17 00:00:00 2001 From: Oren Givon Date: Thu, 14 Jan 2016 10:24:10 +0200 Subject: [PATCH 3/9] iwlwifi: update support for 3168 series firmware and NVM Update the struct which defines the support for 3168 cards. Now it will search for a firmware of this format: iwlwifi-3168-XX.ucode Also, set the minimum version of the ucode to 20. Update the minimum NVM version and minimum NVM calibrations version of the 3168 series. Signed-off-by: Oren Givon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-7000.c | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c index e60cf141ed79..fa41a5e1c890 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c @@ -74,16 +74,19 @@ #define IWL7260_UCODE_API_MAX 17 #define IWL7265_UCODE_API_MAX 17 #define IWL7265D_UCODE_API_MAX 20 +#define IWL3168_UCODE_API_MAX 20 /* Oldest version we won't warn about */ #define IWL7260_UCODE_API_OK 13 #define IWL7265_UCODE_API_OK 13 #define IWL7265D_UCODE_API_OK 13 +#define IWL3168_UCODE_API_OK 20 /* Lowest firmware API version supported */ #define IWL7260_UCODE_API_MIN 13 #define IWL7265_UCODE_API_MIN 13 #define IWL7265D_UCODE_API_MIN 13 +#define IWL3168_UCODE_API_MIN 20 /* NVM versions */ #define IWL7260_NVM_VERSION 0x0a1d @@ -92,6 +95,8 @@ #define IWL3160_TX_POWER_VERSION 0xffff /* meaningless */ #define IWL3165_NVM_VERSION 0x709 #define IWL3165_TX_POWER_VERSION 0xffff /* meaningless */ +#define IWL3168_NVM_VERSION 0xd01 +#define IWL3168_TX_POWER_VERSION 0xffff /* meaningless */ #define IWL7265_NVM_VERSION 0x0a1d #define IWL7265_TX_POWER_VERSION 0xffff /* meaningless */ #define IWL7265D_NVM_VERSION 0x0c11 @@ -109,6 +114,9 @@ #define IWL3160_FW_PRE "iwlwifi-3160-" #define IWL3160_MODULE_FIRMWARE(api) IWL3160_FW_PRE __stringify(api) ".ucode" +#define IWL3168_FW_PRE "iwlwifi-3168-" +#define IWL3168_MODULE_FIRMWARE(api) IWL3168_FW_PRE __stringify(api) ".ucode" + #define IWL7265_FW_PRE "iwlwifi-7265-" #define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode" @@ -180,6 +188,12 @@ static const struct iwl_ht_params iwl7000_ht_params = { .ucode_api_ok = IWL7265_UCODE_API_OK, \ .ucode_api_min = IWL7265_UCODE_API_MIN +#define IWL_DEVICE_3008 \ + IWL_DEVICE_7000_COMMON, \ + .ucode_api_max = IWL3168_UCODE_API_MAX, \ + .ucode_api_ok = IWL3168_UCODE_API_OK, \ + .ucode_api_min = IWL3168_UCODE_API_MIN + #define IWL_DEVICE_7005D \ IWL_DEVICE_7000_COMMON, \ .ucode_api_max = IWL7265D_UCODE_API_MAX, \ @@ -299,11 +313,11 @@ const struct iwl_cfg iwl3165_2ac_cfg = { const struct iwl_cfg iwl3168_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 3168", - .fw_name_pre = IWL7265D_FW_PRE, - IWL_DEVICE_7000, + .fw_name_pre = IWL3168_FW_PRE, + IWL_DEVICE_3008, .ht_params = &iwl7000_ht_params, - .nvm_ver = IWL3165_NVM_VERSION, - .nvm_calib_ver = IWL3165_TX_POWER_VERSION, + .nvm_ver = IWL3168_NVM_VERSION, + .nvm_calib_ver = IWL3168_TX_POWER_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, .dccm_len = IWL7265_DCCM_LEN, }; @@ -376,5 +390,6 @@ const struct iwl_cfg iwl7265d_n_cfg = { MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); +MODULE_FIRMWARE(IWL3168_MODULE_FIRMWARE(IWL3168_UCODE_API_OK)); MODULE_FIRMWARE(IWL7265_MODULE_FIRMWARE(IWL7265_UCODE_API_OK)); MODULE_FIRMWARE(IWL7265D_MODULE_FIRMWARE(IWL7265D_UCODE_API_OK)); From 69c7fda40921c125eeeef6a827f6270ac6aa1baa Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Tue, 29 Dec 2015 11:26:35 +0200 Subject: [PATCH 4/9] iwlwifi: mvm: rs: fix TPC statistics handling FW behaviour changed and now updates driver about the used TPC reduction in the following cases: 1. In tx response, which is used mostly for a single frame case 2. In BA notification When tx aggregation fails with the initial rate, FW will send to the driver BA notification and will try to transmit with the next rate, but this time without tx power reduction. Thus, in case of a failure with the initial rate, driver will get two BA notifications, the first one with reduced tx power as in the LQ command and the second one with 0 power reduction. This patch adapts the TPC statistics according to the description above: 1. Use BA notifications instead of Tx response 2. For TPC only, drop the optimization which considers empty BA as one MPDU. The reason is that with TPC we want to recover very quickly from a bad power reduction and, therefore we'd like the success ratio to get an immediate hit when failing to get a BA, so we'd switch back to a lower or zero power reduction Signed-off-by: Gregory Greenman Signed-off-by: Emmanuel Grumbach --- .../wireless/intel/iwlwifi/mvm/fw-api-tx.h | 6 +- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 74 ++++++++++++------- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 5 +- 3 files changed, 56 insertions(+), 29 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h index 0036d18334af..ba3f0bbddde8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h @@ -510,6 +510,9 @@ struct iwl_mvm_tx_resp { * @scd_ssn: the index of the last contiguously sent packet * @txed: number of Txed frames in this batch * @txed_2_done: number of Acked frames in this batch + * @reduced_txp: power reduced according to TPC. This is the actual value and + * not a copy from the LQ command. Thus, if not the first rate was used + * for Tx-ing then this value will be set to 0 by FW. */ struct iwl_mvm_ba_notif { __le32 sta_addr_lo32; @@ -524,7 +527,8 @@ struct iwl_mvm_ba_notif { __le16 scd_ssn; u8 txed; u8 txed_2_done; - __le16 reserved1; + u8 reduced_txp; + u8 reserved1; } __packed; /* diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 7bb6fd0e4391..94caa88df442 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -2,6 +2,7 @@ * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 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 @@ -724,14 +725,28 @@ static int _rs_collect_tx_data(struct iwl_mvm *mvm, return 0; } -static int rs_collect_tx_data(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, - int scale_index, int attempts, int successes, - u8 reduced_txp) +static int rs_collect_tpc_data(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, + int scale_index, int attempts, int successes, + u8 reduced_txp) +{ + struct iwl_rate_scale_data *window = NULL; + + if (WARN_ON_ONCE(reduced_txp > TPC_MAX_REDUCTION)) + return -EINVAL; + + window = &tbl->tpc_win[reduced_txp]; + return _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes, + window); +} + +static int rs_collect_tlc_data(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, + int scale_index, int attempts, int successes) { struct iwl_rate_scale_data *window = NULL; - int ret; if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) return -EINVAL; @@ -745,16 +760,6 @@ static int rs_collect_tx_data(struct iwl_mvm *mvm, /* Select window for current tx bit rate */ window = &(tbl->win[scale_index]); - - ret = _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes, - window); - if (ret) - return ret; - - if (WARN_ON_ONCE(reduced_txp > TPC_MAX_REDUCTION)) - return -EINVAL; - - window = &tbl->tpc_win[reduced_txp]; return _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes, window); } @@ -1301,17 +1306,30 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, * first index into rate scale table. */ if (info->flags & IEEE80211_TX_STAT_AMPDU) { - /* ampdu_ack_len = 0 marks no BA was received. In this case - * treat it as a single frame loss as we don't want the success - * ratio to dip too quickly because a BA wasn't received + rs_collect_tpc_data(mvm, lq_sta, curr_tbl, lq_rate.index, + info->status.ampdu_len, + info->status.ampdu_ack_len, + reduced_txp); + + /* ampdu_ack_len = 0 marks no BA was received. For TLC, treat + * it as a single frame loss as we don't want the success ratio + * to dip too quickly because a BA wasn't received. + * For TPC, there's no need for this optimisation since we want + * to recover very quickly from a bad power reduction and, + * therefore we'd like the success ratio to get an immediate hit + * when failing to get a BA, so we'd switch back to a lower or + * zero power reduction. When FW transmits agg with a rate + * different from the initial rate, it will not use reduced txp + * and will send BA notification twice (one empty with reduced + * txp equal to the value from LQ and one with reduced txp 0). + * We need to update counters for each txp level accordingly. */ if (info->status.ampdu_ack_len == 0) info->status.ampdu_len = 1; - rs_collect_tx_data(mvm, lq_sta, curr_tbl, lq_rate.index, - info->status.ampdu_len, - info->status.ampdu_ack_len, - reduced_txp); + rs_collect_tlc_data(mvm, lq_sta, curr_tbl, lq_rate.index, + info->status.ampdu_len, + info->status.ampdu_ack_len); /* Update success/fail counts if not searching for new mode */ if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { @@ -1344,9 +1362,13 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, else continue; - rs_collect_tx_data(mvm, lq_sta, tmp_tbl, lq_rate.index, - 1, i < retries ? 0 : legacy_success, - reduced_txp); + rs_collect_tpc_data(mvm, lq_sta, tmp_tbl, + lq_rate.index, 1, + i < retries ? 0 : legacy_success, + reduced_txp); + rs_collect_tlc_data(mvm, lq_sta, tmp_tbl, + lq_rate.index, 1, + i < retries ? 0 : legacy_success); } /* Update success/fail counts if not searching for new mode */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 8bf48a7d0f4e..0914ec2fd574 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1029,7 +1029,6 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); mvmsta->tid_data[tid].rate_n_flags = le32_to_cpu(tx_resp->initial_rate); - mvmsta->tid_data[tid].reduced_tpc = tx_resp->reduced_tpc; mvmsta->tid_data[tid].tx_time = le16_to_cpu(tx_resp->wireless_media_time); } @@ -1060,7 +1059,7 @@ static void iwl_mvm_tx_info_from_ba_notif(struct ieee80211_tx_info *info, /* TODO: not accounted if the whole A-MPDU failed */ info->status.tx_time = tid_data->tx_time; info->status.status_driver_data[0] = - (void *)(uintptr_t)tid_data->reduced_tpc; + (void *)(uintptr_t)ba_notif->reduced_txp; info->status.status_driver_data[1] = (void *)(uintptr_t)tid_data->rate_n_flags; } @@ -1133,6 +1132,8 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) scd_flow, ba_resp_scd_ssn, ba_notif->txed, ba_notif->txed_2_done); + IWL_DEBUG_TX_REPLY(mvm, "reduced txp from ba notif %d\n", + ba_notif->reduced_txp); tid_data->next_reclaimed = ba_resp_scd_ssn; iwl_mvm_check_ratid_empty(mvm, sta, tid); From 7e502e5bc546a8d8be188fa019fe6fcdf02e3c87 Mon Sep 17 00:00:00 2001 From: Oren Givon Date: Mon, 25 Jan 2016 12:00:42 +0200 Subject: [PATCH 5/9] iwlwifi: fix name of ucode loaded for 8265 series Fix the name of the ucode being loaded for 8265 series to be: iwlwifi-8265-XX.ucode Signed-off-by: Oren Givon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-8000.c | 42 +++++++++++++++---- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 6 ++- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c index c84a0299d43e..bce9b3420a13 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c @@ -7,6 +7,7 @@ * * Copyright(c) 2014 Intel Corporation. All rights reserved. * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 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 @@ -70,12 +71,15 @@ /* Highest firmware API version supported */ #define IWL8000_UCODE_API_MAX 20 +#define IWL8265_UCODE_API_MAX 20 /* Oldest version we won't warn about */ #define IWL8000_UCODE_API_OK 13 +#define IWL8265_UCODE_API_OK 20 /* Lowest firmware API version supported */ #define IWL8000_UCODE_API_MIN 13 +#define IWL8265_UCODE_API_MIN 20 /* NVM versions */ #define IWL8000_NVM_VERSION 0x0a1d @@ -93,6 +97,10 @@ #define IWL8000_MODULE_FIRMWARE(api) \ IWL8000_FW_PRE "-" __stringify(api) ".ucode" +#define IWL8265_FW_PRE "iwlwifi-8265-" +#define IWL8265_MODULE_FIRMWARE(api) \ + IWL8265_FW_PRE __stringify(api) ".ucode" + #define NVM_HW_SECTION_NUM_FAMILY_8000 10 #define DEFAULT_NVM_FILE_FAMILY_8000B "nvmData-8000B" #define DEFAULT_NVM_FILE_FAMILY_8000C "nvmData-8000C" @@ -144,10 +152,7 @@ static const struct iwl_tt_params iwl8000_tt_params = { .support_tx_backoff = true, }; -#define IWL_DEVICE_8000 \ - .ucode_api_max = IWL8000_UCODE_API_MAX, \ - .ucode_api_ok = IWL8000_UCODE_API_OK, \ - .ucode_api_min = IWL8000_UCODE_API_MIN, \ +#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, \ @@ -167,10 +172,28 @@ static const struct iwl_tt_params iwl8000_tt_params = { .thermal_params = &iwl8000_tt_params, \ .apmg_not_supported = true +#define IWL_DEVICE_8000 \ + IWL_DEVICE_8000_COMMON, \ + .ucode_api_max = IWL8000_UCODE_API_MAX, \ + .ucode_api_ok = IWL8000_UCODE_API_OK, \ + .ucode_api_min = IWL8000_UCODE_API_MIN \ + +#define IWL_DEVICE_8260 \ + IWL_DEVICE_8000_COMMON, \ + .ucode_api_max = IWL8000_UCODE_API_MAX, \ + .ucode_api_ok = IWL8000_UCODE_API_OK, \ + .ucode_api_min = IWL8000_UCODE_API_MIN \ + +#define IWL_DEVICE_8265 \ + IWL_DEVICE_8000_COMMON, \ + .ucode_api_max = IWL8265_UCODE_API_MAX, \ + .ucode_api_ok = IWL8265_UCODE_API_OK, \ + .ucode_api_min = IWL8265_UCODE_API_MIN \ + const struct iwl_cfg iwl8260_2n_cfg = { .name = "Intel(R) Dual Band Wireless N 8260", .fw_name_pre = IWL8000_FW_PRE, - IWL_DEVICE_8000, + IWL_DEVICE_8260, .ht_params = &iwl8000_ht_params, .nvm_ver = IWL8000_NVM_VERSION, .nvm_calib_ver = IWL8000_TX_POWER_VERSION, @@ -179,7 +202,7 @@ const struct iwl_cfg iwl8260_2n_cfg = { const struct iwl_cfg iwl8260_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 8260", .fw_name_pre = IWL8000_FW_PRE, - IWL_DEVICE_8000, + IWL_DEVICE_8260, .ht_params = &iwl8000_ht_params, .nvm_ver = IWL8000_NVM_VERSION, .nvm_calib_ver = IWL8000_TX_POWER_VERSION, @@ -188,8 +211,8 @@ const struct iwl_cfg iwl8260_2ac_cfg = { const struct iwl_cfg iwl8265_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 8265", - .fw_name_pre = IWL8000_FW_PRE, - IWL_DEVICE_8000, + .fw_name_pre = IWL8265_FW_PRE, + IWL_DEVICE_8265, .ht_params = &iwl8000_ht_params, .nvm_ver = IWL8000_NVM_VERSION, .nvm_calib_ver = IWL8000_TX_POWER_VERSION, @@ -209,7 +232,7 @@ const struct iwl_cfg iwl4165_2ac_cfg = { const struct iwl_cfg iwl8260_2ac_sdio_cfg = { .name = "Intel(R) Dual Band Wireless-AC 8260", .fw_name_pre = IWL8000_FW_PRE, - IWL_DEVICE_8000, + IWL_DEVICE_8260, .ht_params = &iwl8000_ht_params, .nvm_ver = IWL8000_NVM_VERSION, .nvm_calib_ver = IWL8000_TX_POWER_VERSION, @@ -236,3 +259,4 @@ const struct iwl_cfg iwl4165_2ac_sdio_cfg = { }; MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK)); +MODULE_FIRMWARE(IWL8265_MODULE_FIRMWARE(IWL8265_UCODE_API_OK)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 7acb49075683..ab4c2a0470b2 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -243,8 +243,10 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first) if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { char rev_step = 'A' + CSR_HW_REV_STEP(drv->trans->hw_rev); - snprintf(drv->firmware_name, sizeof(drv->firmware_name), - "%s%c-%s.ucode", name_pre, rev_step, tag); + if (rev_step != 'A') + snprintf(drv->firmware_name, + sizeof(drv->firmware_name), "%s%c-%s.ucode", + name_pre, rev_step, tag); } IWL_DEBUG_INFO(drv, "attempting to load firmware %s'%s'\n", From 0ad4ece5bbd4d2104564c7dbd477ae0c729d4fcc Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 10 Feb 2016 18:13:30 +0200 Subject: [PATCH 6/9] iwlwifi: dvm: remove a wrong dependency on m This was wronly added when the dependency on IWLWIFI was removed. This fixes https://bugzilla.kernel.org/show_bug.cgi?id=112201 Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/Kconfig b/drivers/net/wireless/intel/iwlwifi/Kconfig index 866067789330..7438fbeef744 100644 --- a/drivers/net/wireless/intel/iwlwifi/Kconfig +++ b/drivers/net/wireless/intel/iwlwifi/Kconfig @@ -53,7 +53,6 @@ config IWLWIFI_LEDS config IWLDVM tristate "Intel Wireless WiFi DVM Firmware support" - depends on m help This is the driver that supports the DVM firmware. The list of the devices that use this firmware is available here: From 5e56276e7555b34550d51459a801ff75eca8b907 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 2 Feb 2016 15:11:15 +0200 Subject: [PATCH 7/9] iwlwifi: mvm: don't allow sched scans without matches to be started The firmware can perform a scheduled scan with not matchsets passed, but it can't send notification that results were found. Since the userspace then cannot know when we got new results and the firmware wouldn't trigger a wake in case we are sleeping, it's better not to allow scans without matchsets. This fixes https://bugzilla.kernel.org/show_bug.cgi?id=110831 Cc: [3.17+] Signed-off-by: Luca Coelho Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 9a15642f80dd..ea1e177c2ea1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -1298,6 +1298,10 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, return -EBUSY; } + /* we don't support "match all" in the firmware */ + if (!req->n_match_sets) + return -EOPNOTSUPP; + ret = iwl_mvm_check_running_scans(mvm, type); if (ret) return ret; From a6bd005fe92dc1cc808c4c6aa43e3b2a8272bbfa Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 31 Jan 2016 15:02:30 +0200 Subject: [PATCH 8/9] iwlwifi: pcie: fix RF-Kill vs. firmware load race When we load the firmware, we hold trans_pcie->mutex to avoid nested flows. We also rely on the ISR to wake up the thread when the DMA has finished copying a chunk. During this flow, we enable the RF-Kill interrupt. The problem is that the RF-Kill interrupt handler can take the mutex and bring the device down. This means that if we load the firmware while the RF-Kill switch is enabled (which will happen when we load the INIT firmware to read the device's capabilities and register to mac80211), we may get an RF-Kill interrupt immediately and the ISR will be waiting for the mutex held by the thread that is currently loading the firmware. At this stage, the ISR won't be able to service the DMA's interrupt needed to wake up the thread that load the firmware. We are in a deadlock situation which ends when the thread that loads the firmware fails on timeout and releases the mutex. To fix this, take the mutex later in the flow, disable the interrupts and synchronize_irq() to give a chance to the RF-Kill interrupt to run and complete. After that, mask all the interrupts besides the DMA interrupt and proceed with firmware load. Make sure to check that there was no RF-Kill interrupt when the interrupts were disabled. This fixes https://bugzilla.kernel.org/show_bug.cgi?id=111361 Signed-off-by: Emmanuel Grumbach --- .../wireless/intel/iwlwifi/pcie/internal.h | 9 + drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 8 +- .../net/wireless/intel/iwlwifi/pcie/trans.c | 188 ++++++++++-------- 3 files changed, 123 insertions(+), 82 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index cc3888e2700d..73c95594eabe 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -490,6 +490,15 @@ static inline void iwl_enable_interrupts(struct iwl_trans *trans) iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); } +static inline void iwl_enable_fw_load_int(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + IWL_DEBUG_ISR(trans, "Enabling FW load interrupt\n"); + trans_pcie->inta_mask = CSR_INT_BIT_FH_TX; + iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); +} + static inline void iwl_enable_rfkill_int(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index ccafbd8cf4b3..152cf9ad9566 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -1438,9 +1438,11 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) inta & ~trans_pcie->inta_mask); } - /* Re-enable all interrupts */ - /* only Re-enable if disabled by irq */ - if (test_bit(STATUS_INT_ENABLED, &trans->status)) + /* we are loading the firmware, enable FH_TX interrupt only */ + if (handled & CSR_INT_BIT_FH_TX) + iwl_enable_fw_load_int(trans); + /* only Re-enable all interrupt if disabled by irq */ + else if (test_bit(STATUS_INT_ENABLED, &trans->status)) iwl_enable_interrupts(trans); /* Re-enable RF_KILL if it occurred */ else if (handled & CSR_INT_BIT_RF_KILL) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index d60a467a983c..55030729c7d9 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1021,82 +1021,6 @@ static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans, &first_ucode_section); } -static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, - const struct fw_img *fw, bool run_in_rfkill) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - bool hw_rfkill; - int ret; - - mutex_lock(&trans_pcie->mutex); - - /* Someone called stop_device, don't try to start_fw */ - if (trans_pcie->is_down) { - IWL_WARN(trans, - "Can't start_fw since the HW hasn't been started\n"); - ret = EIO; - goto out; - } - - /* This may fail if AMT took ownership of the device */ - if (iwl_pcie_prepare_card_hw(trans)) { - IWL_WARN(trans, "Exit HW not ready\n"); - ret = -EIO; - goto out; - } - - iwl_enable_rfkill_int(trans); - - /* If platform's RF_KILL switch is NOT set to KILL */ - hw_rfkill = iwl_is_rfkill_set(trans); - if (hw_rfkill) - set_bit(STATUS_RFKILL, &trans->status); - else - clear_bit(STATUS_RFKILL, &trans->status); - iwl_trans_pcie_rf_kill(trans, hw_rfkill); - if (hw_rfkill && !run_in_rfkill) { - ret = -ERFKILL; - goto out; - } - - iwl_write32(trans, CSR_INT, 0xFFFFFFFF); - - ret = iwl_pcie_nic_init(trans); - if (ret) { - IWL_ERR(trans, "Unable to init nic\n"); - goto out; - } - - /* make sure rfkill handshake bits are cleared */ - iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); - - /* clear (again), then enable host interrupts */ - iwl_write32(trans, CSR_INT, 0xFFFFFFFF); - iwl_enable_interrupts(trans); - - /* really make sure rfkill handshake bits are cleared */ - iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - - /* Load the given image to the HW */ - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) - ret = iwl_pcie_load_given_ucode_8000(trans, fw); - else - ret = iwl_pcie_load_given_ucode(trans, fw); - -out: - mutex_unlock(&trans_pcie->mutex); - return ret; -} - -static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr) -{ - iwl_pcie_reset_ict(trans); - iwl_pcie_tx_start(trans, scd_addr); -} - static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -1127,7 +1051,8 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) * already dead. */ if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) { - IWL_DEBUG_INFO(trans, "DEVICE_ENABLED bit was set and is now cleared\n"); + IWL_DEBUG_INFO(trans, + "DEVICE_ENABLED bit was set and is now cleared\n"); iwl_pcie_tx_stop(trans); iwl_pcie_rx_stop(trans); @@ -1161,7 +1086,6 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) iwl_disable_interrupts(trans); spin_unlock(&trans_pcie->irq_lock); - /* clear all status bits */ clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); clear_bit(STATUS_INT_ENABLED, &trans->status); @@ -1194,10 +1118,116 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) if (hw_rfkill != was_hw_rfkill) iwl_trans_pcie_rf_kill(trans, hw_rfkill); - /* re-take ownership to prevent other users from stealing the deivce */ + /* re-take ownership to prevent other users from stealing the device */ iwl_pcie_prepare_card_hw(trans); } +static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, + const struct fw_img *fw, bool run_in_rfkill) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + bool hw_rfkill; + int ret; + + /* This may fail if AMT took ownership of the device */ + if (iwl_pcie_prepare_card_hw(trans)) { + IWL_WARN(trans, "Exit HW not ready\n"); + ret = -EIO; + goto out; + } + + iwl_enable_rfkill_int(trans); + + iwl_write32(trans, CSR_INT, 0xFFFFFFFF); + + /* + * We enabled the RF-Kill interrupt and the handler may very + * well be running. Disable the interrupts to make sure no other + * interrupt can be fired. + */ + iwl_disable_interrupts(trans); + + /* Make sure it finished running */ + synchronize_irq(trans_pcie->pci_dev->irq); + + mutex_lock(&trans_pcie->mutex); + + /* If platform's RF_KILL switch is NOT set to KILL */ + hw_rfkill = iwl_is_rfkill_set(trans); + if (hw_rfkill) + set_bit(STATUS_RFKILL, &trans->status); + else + clear_bit(STATUS_RFKILL, &trans->status); + iwl_trans_pcie_rf_kill(trans, hw_rfkill); + if (hw_rfkill && !run_in_rfkill) { + ret = -ERFKILL; + goto out; + } + + /* Someone called stop_device, don't try to start_fw */ + if (trans_pcie->is_down) { + IWL_WARN(trans, + "Can't start_fw since the HW hasn't been started\n"); + ret = EIO; + goto out; + } + + /* make sure rfkill handshake bits are cleared */ + iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + /* clear (again), then enable host interrupts */ + iwl_write32(trans, CSR_INT, 0xFFFFFFFF); + + ret = iwl_pcie_nic_init(trans); + if (ret) { + IWL_ERR(trans, "Unable to init nic\n"); + goto out; + } + + /* + * Now, we load the firmware and don't want to be interrupted, even + * by the RF-Kill interrupt (hence mask all the interrupt besides the + * FH_TX interrupt which is needed to load the firmware). If the + * RF-Kill switch is toggled, we will find out after having loaded + * the firmware and return the proper value to the caller. + */ + iwl_enable_fw_load_int(trans); + + /* really make sure rfkill handshake bits are cleared */ + iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + + /* Load the given image to the HW */ + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + ret = iwl_pcie_load_given_ucode_8000(trans, fw); + else + ret = iwl_pcie_load_given_ucode(trans, fw); + iwl_enable_interrupts(trans); + + /* re-check RF-Kill state since we may have missed the interrupt */ + hw_rfkill = iwl_is_rfkill_set(trans); + if (hw_rfkill) + set_bit(STATUS_RFKILL, &trans->status); + else + clear_bit(STATUS_RFKILL, &trans->status); + + iwl_trans_pcie_rf_kill(trans, hw_rfkill); + if (hw_rfkill && !run_in_rfkill) + ret = -ERFKILL; + +out: + mutex_unlock(&trans_pcie->mutex); + return ret; +} + +static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr) +{ + iwl_pcie_reset_ict(trans); + iwl_pcie_tx_start(trans, scd_addr); +} + static void iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); From 20aa99bbddae74bded68338f9ba200ccae02858b Mon Sep 17 00:00:00 2001 From: Anton Protopopov Date: Thu, 11 Feb 2016 08:35:15 +0200 Subject: [PATCH 9/9] iwlwifi: pcie: fix erroneous return value The iwl_trans_pcie_start_fw() function may return the positive value EIO instead of -EIO in case of error. Signed-off-by: Anton Protopopov Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 55030729c7d9..5a854c609477 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1168,7 +1168,7 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, if (trans_pcie->is_down) { IWL_WARN(trans, "Can't start_fw since the HW hasn't been started\n"); - ret = EIO; + ret = -EIO; goto out; }