From 6d48fcd900803c7ddb9b3813176e2cdb4fcd6aaa Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 28 Apr 2016 14:39:22 +0200 Subject: [PATCH 01/56] iwlwifi: remove useless enum values Since the values of this enum are used only internally, we can let the compiler number them. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 5c5b9f228ac6..26afa72daa94 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -129,8 +129,8 @@ struct iwl_drv { }; enum { - DVM_OP_MODE = 0, - MVM_OP_MODE = 1, + DVM_OP_MODE, + MVM_OP_MODE, }; /* Protects the table contents, i.e. the ops pointer & drv list */ From ca221c9b946cd4a9ea67375c8d90379a0e65179d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 28 Apr 2016 14:40:59 +0200 Subject: [PATCH 02/56] iwlwifi: change fw.mvm_fw to fw.type Instead of explicitly indicating the difference between just DVM and MVM with an mvm_fw boolean change this to fw.type to be more extensible and easier to understand. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 26 ++++++++++++-------- drivers/net/wireless/intel/iwlwifi/iwl-fw.h | 14 +++++++++-- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 26afa72daa94..e2df544cc64e 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -795,17 +795,17 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, case IWL_UCODE_TLV_SEC_RT: iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR, tlv_len); - drv->fw.mvm_fw = true; + drv->fw.type = IWL_FW_MVM; break; case IWL_UCODE_TLV_SEC_INIT: iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT, tlv_len); - drv->fw.mvm_fw = true; + drv->fw.type = IWL_FW_MVM; break; case IWL_UCODE_TLV_SEC_WOWLAN: iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN, tlv_len); - drv->fw.mvm_fw = true; + drv->fw.type = IWL_FW_MVM; break; case IWL_UCODE_TLV_DEF_CALIB: if (tlv_len != sizeof(struct iwl_tlv_calib_data)) @@ -827,17 +827,17 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, case IWL_UCODE_TLV_SECURE_SEC_RT: iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR, tlv_len); - drv->fw.mvm_fw = true; + drv->fw.type = IWL_FW_MVM; break; case IWL_UCODE_TLV_SECURE_SEC_INIT: iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT, tlv_len); - drv->fw.mvm_fw = true; + drv->fw.type = IWL_FW_MVM; break; case IWL_UCODE_TLV_SECURE_SEC_WOWLAN: iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN, tlv_len); - drv->fw.mvm_fw = true; + drv->fw.type = IWL_FW_MVM; break; case IWL_UCODE_TLV_NUM_OF_CPU: if (tlv_len != sizeof(u32)) @@ -1272,7 +1272,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) * In mvm uCode there is no difference between data and instructions * sections. */ - if (!fw->mvm_fw && validate_sec_sizes(drv, pieces, drv->cfg)) + if (fw->type == IWL_FW_DVM && validate_sec_sizes(drv, pieces, drv->cfg)) goto try_again; /* Allocate ucode buffers for card's bus-master loading ... */ @@ -1400,10 +1400,16 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) release_firmware(ucode_raw); mutex_lock(&iwlwifi_opmode_table_mtx); - if (fw->mvm_fw) - op = &iwlwifi_opmode_table[MVM_OP_MODE]; - else + switch (fw->type) { + case IWL_FW_DVM: op = &iwlwifi_opmode_table[DVM_OP_MODE]; + break; + default: + WARN(1, "Invalid fw type %d\n", fw->type); + case IWL_FW_MVM: + op = &iwlwifi_opmode_table[MVM_OP_MODE]; + break; + } IWL_INFO(drv, "loaded firmware version %s op_mode %s\n", drv->fw.fw_version, op->name); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h index e461d631893a..655ec52a0c03 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h @@ -230,6 +230,16 @@ struct iwl_gscan_capabilities { u32 max_number_of_black_listed_ssid; }; +/** + * enum iwl_fw_type - iwlwifi firmware type + * @IWL_FW_DVM: DVM firmware + * @IWL_FW_MVM: MVM firmware + */ +enum iwl_fw_type { + IWL_FW_DVM, + IWL_FW_MVM, +}; + /** * struct iwl_fw - variables associated with the firmware * @@ -244,7 +254,7 @@ struct iwl_gscan_capabilities { * @inst_evtlog_ptr: event log offset for runtime ucode. * @inst_evtlog_size: event log size for runtime ucode. * @inst_errlog_ptr: error log offfset for runtime ucode. - * @mvm_fw: indicates this is MVM firmware + * @type: firmware type (&enum iwl_fw_type) * @cipher_scheme: optional external cipher scheme. * @human_readable: human readable version * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until @@ -275,7 +285,7 @@ struct iwl_fw { u8 valid_tx_ant; u8 valid_rx_ant; - bool mvm_fw; + enum iwl_fw_type type; struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS]; u8 human_readable[FW_VER_HUMAN_READABLE_SZ]; From 9794c64f302d6d544acbb5ab69a327d694a70fcb Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Wed, 19 Aug 2015 17:34:28 +0300 Subject: [PATCH 03/56] iwlwifi: mvm: support dqa queue inactivation upon timeout Support marking queues as inactive upon a timeout expiring, and allow inactive queues to be re-assigned to other RA/TIDs if no other queue is free. This is done by keeping a timestamp of the latest frame TXed for every RA/TID, and then going over the queues currently in use when a new queue is needed, inactivating all those that are inactive. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 17 +- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 197 +++++++++++++++++- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 7 + drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 26 ++- .../net/wireless/intel/iwlwifi/mvm/utils.c | 166 ++++++++++++++- 5 files changed, 398 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 4b75b9226898..bf7d78e0e14a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -687,13 +687,22 @@ struct iwl_mvm_baid_data { * This is the state of a queue that has been fully configured (including * SCD pointers, etc), has a specific RA/TID assigned to it, and can be * used to send traffic. + * @IWL_MVM_QUEUE_INACTIVE: queue is allocated but no traffic on it + * This is a state of a queue that has had traffic on it, but during the + * last %IWL_MVM_DQA_QUEUE_TIMEOUT time period there has been no traffic on + * it. In this state, when a new queue is needed to be allocated but no + * such free queue exists, an inactive queue might be freed and given to + * the new RA/TID. */ enum iwl_mvm_queue_status { IWL_MVM_QUEUE_FREE, IWL_MVM_QUEUE_RESERVED, IWL_MVM_QUEUE_READY, + IWL_MVM_QUEUE_INACTIVE, }; +#define IWL_MVM_DQA_QUEUE_TIMEOUT (5 * HZ) + struct iwl_mvm { /* for logger access */ struct device *dev; @@ -750,11 +759,15 @@ struct iwl_mvm { u32 hw_queue_to_mac80211; u8 hw_queue_refcount; u8 ra_sta_id; /* The RA this queue is mapped to, if exists */ + bool reserved; /* Is this the TXQ reserved for a STA */ u16 tid_bitmap; /* Bitmap of the TIDs mapped to this queue */ + /* Timestamp for inactivation per TID of this queue */ + unsigned long last_frame_time[IWL_MAX_TID_COUNT + 1]; enum iwl_mvm_queue_status status; } queue_info[IWL_MAX_HW_QUEUES]; spinlock_t queue_info_lock; /* For syncing queue mgmt operations */ struct work_struct add_stream_wk; /* To add streams to queues */ + atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES]; const char *nvm_file_name; @@ -1618,7 +1631,7 @@ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, */ void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, u8 tid, u8 flags); -int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq); +int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id, u8 minq, u8 maxq); /* Return a bitmask with all the hw supported queues, except for the * command queue, which can't be flushed. @@ -1725,6 +1738,8 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, void iwl_mvm_reorder_timer_expired(unsigned long data); struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm); +void iwl_mvm_inactivity_check(struct iwl_mvm *mvm); + 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/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 64b07b114d3c..84384a4321d9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -310,6 +310,112 @@ static void iwl_mvm_tdls_sta_deinit(struct iwl_mvm *mvm, iwl_mvm_disable_txq(mvm, i, i, IWL_MAX_TID_COUNT, 0); } +/* Disable aggregations for a bitmap of TIDs for a given station */ +static int iwl_mvm_invalidate_sta_queue(struct iwl_mvm *mvm, int queue, + unsigned long disable_agg_tids, + bool remove_queue) +{ + struct iwl_mvm_add_sta_cmd cmd = {}; + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + u32 status; + u8 sta_id; + int ret; + + spin_lock_bh(&mvm->queue_info_lock); + sta_id = mvm->queue_info[queue].ra_sta_id; + spin_unlock_bh(&mvm->queue_info_lock); + + rcu_read_lock(); + + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + + if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { + rcu_read_unlock(); + return -EINVAL; + } + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + + mvmsta->tid_disable_agg |= disable_agg_tids; + + cmd.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color); + cmd.sta_id = mvmsta->sta_id; + cmd.add_modify = STA_MODE_MODIFY; + cmd.modify_mask = STA_MODIFY_QUEUES; + if (disable_agg_tids) + cmd.modify_mask |= STA_MODIFY_TID_DISABLE_TX; + if (remove_queue) + cmd.modify_mask |= STA_MODIFY_QUEUE_REMOVAL; + cmd.tfd_queue_msk = cpu_to_le32(mvmsta->tfd_queue_msk); + cmd.tid_disable_tx = cpu_to_le16(mvmsta->tid_disable_agg); + + rcu_read_unlock(); + + /* Notify FW of queue removal from the STA queues */ + status = ADD_STA_SUCCESS; + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, + iwl_mvm_add_sta_cmd_size(mvm), + &cmd, &status); + + return ret; +} + +/* + * Remove a queue from a station's resources. + * Note that this only marks as free. It DOESN'T delete a BA agreement, and + * doesn't disable the queue + */ +static int iwl_mvm_remove_sta_queue_marking(struct iwl_mvm *mvm, int queue) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + unsigned long tid_bitmap; + unsigned long disable_agg_tids = 0; + u8 sta_id; + int tid; + + lockdep_assert_held(&mvm->mutex); + + spin_lock_bh(&mvm->queue_info_lock); + sta_id = mvm->queue_info[queue].ra_sta_id; + tid_bitmap = mvm->queue_info[queue].tid_bitmap; + spin_unlock_bh(&mvm->queue_info_lock); + + rcu_read_lock(); + + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + + if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { + rcu_read_unlock(); + return 0; + } + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + + spin_lock_bh(&mvmsta->lock); + for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { + mvmsta->tid_data[tid].txq_id = IEEE80211_INVAL_HW_QUEUE; + + if (mvmsta->tid_data[tid].state == IWL_AGG_ON) + disable_agg_tids |= BIT(tid); + } + mvmsta->tfd_queue_msk &= ~BIT(queue); /* Don't use this queue anymore */ + + spin_unlock_bh(&mvmsta->lock); + + rcu_read_unlock(); + + spin_lock(&mvm->queue_info_lock); + /* Unmap MAC queues and TIDs from this queue */ + mvm->queue_info[queue].hw_queue_to_mac80211 = 0; + mvm->queue_info[queue].hw_queue_refcount = 0; + mvm->queue_info[queue].tid_bitmap = 0; + spin_unlock(&mvm->queue_info_lock); + + return disable_agg_tids; +} + static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, struct ieee80211_sta *sta, u8 ac, int tid, struct ieee80211_hdr *hdr) @@ -325,6 +431,9 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, iwl_mvm_get_wd_timeout(mvm, mvmsta->vif, false, false); u8 mac_queue = mvmsta->vif->hw_queue[ac]; int queue = -1; + bool using_inactive_queue = false; + unsigned long disable_agg_tids = 0; + enum iwl_mvm_agg_state queue_state; int ssn; int ret; @@ -338,7 +447,8 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, */ if (!ieee80211_is_data_qos(hdr->frame_control) || ieee80211_is_qos_nullfunc(hdr->frame_control)) { - queue = iwl_mvm_find_free_queue(mvm, IWL_MVM_DQA_MIN_MGMT_QUEUE, + queue = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id, + IWL_MVM_DQA_MIN_MGMT_QUEUE, IWL_MVM_DQA_MAX_MGMT_QUEUE); if (queue >= IWL_MVM_DQA_MIN_MGMT_QUEUE) IWL_DEBUG_TX_QUEUES(mvm, "Found free MGMT queue #%d\n", @@ -347,15 +457,36 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, /* If no such queue is found, we'll use a DATA queue instead */ } - if (queue < 0 && mvmsta->reserved_queue != IEEE80211_INVAL_HW_QUEUE) { + if ((queue < 0 && mvmsta->reserved_queue != IEEE80211_INVAL_HW_QUEUE) && + (mvm->queue_info[mvmsta->reserved_queue].status == + IWL_MVM_QUEUE_RESERVED || + mvm->queue_info[mvmsta->reserved_queue].status == + IWL_MVM_QUEUE_INACTIVE)) { queue = mvmsta->reserved_queue; + mvm->queue_info[queue].reserved = true; IWL_DEBUG_TX_QUEUES(mvm, "Using reserved queue #%d\n", queue); } if (queue < 0) - queue = iwl_mvm_find_free_queue(mvm, IWL_MVM_DQA_MIN_DATA_QUEUE, + queue = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id, + IWL_MVM_DQA_MIN_DATA_QUEUE, IWL_MVM_DQA_MAX_DATA_QUEUE); + /* + * Check if this queue is already allocated but inactive. + * In such a case, we'll need to first free this queue before enabling + * it again, so we'll mark it as reserved to make sure no new traffic + * arrives on it + */ + if (queue > 0 && + mvm->queue_info[queue].status == IWL_MVM_QUEUE_INACTIVE) { + mvm->queue_info[queue].status = IWL_MVM_QUEUE_RESERVED; + using_inactive_queue = true; + IWL_DEBUG_TX_QUEUES(mvm, + "Re-assigning TXQ %d: sta_id=%d, tid=%d\n", + queue, mvmsta->sta_id, tid); + } + /* * Mark TXQ as ready, even though it hasn't been fully configured yet, * to make sure no one else takes it. @@ -380,6 +511,38 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, cfg.aggregate = (queue >= IWL_MVM_DQA_MIN_DATA_QUEUE || queue == IWL_MVM_DQA_BSS_CLIENT_QUEUE); + /* + * If this queue was previously inactive (idle) - we need to free it + * first + */ + if (using_inactive_queue) { + struct iwl_scd_txq_cfg_cmd cmd = { + .scd_queue = queue, + .enable = 0, + }; + + disable_agg_tids = iwl_mvm_remove_sta_queue_marking(mvm, queue); + + /* Disable the queue */ + iwl_mvm_invalidate_sta_queue(mvm, queue, disable_agg_tids, + true); + iwl_trans_txq_disable(mvm->trans, queue, false); + ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), + &cmd); + if (ret) { + IWL_ERR(mvm, + "Failed to free inactive queue %d (ret=%d)\n", + queue, ret); + + /* Re-mark the inactive queue as inactive */ + spin_lock_bh(&mvm->queue_info_lock); + mvm->queue_info[queue].status = IWL_MVM_QUEUE_INACTIVE; + spin_unlock_bh(&mvm->queue_info_lock); + + return ret; + } + } + IWL_DEBUG_TX_QUEUES(mvm, "Allocating queue #%d to sta %d on tid %d\n", queue, mvmsta->sta_id, tid); @@ -389,7 +552,9 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, spin_lock_bh(&mvmsta->lock); mvmsta->tid_data[tid].txq_id = queue; + mvmsta->tid_data[tid].is_tid_active = true; mvmsta->tfd_queue_msk |= BIT(queue); + queue_state = mvmsta->tid_data[tid].state; if (mvmsta->reserved_queue == queue) mvmsta->reserved_queue = IEEE80211_INVAL_HW_QUEUE; @@ -399,7 +564,11 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, if (ret) goto out_err; - return 0; + /* If we need to re-enable aggregations... */ + if (queue_state == IWL_AGG_ON) + ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true); + + return ret; out_err: iwl_mvm_disable_txq(mvm, queue, mac_queue, tid, 0); @@ -476,6 +645,9 @@ void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk) unsigned long deferred_tid_traffic; int sta_id, tid; + /* Check inactivity of queues */ + iwl_mvm_inactivity_check(mvm); + mutex_lock(&mvm->mutex); /* Go over all stations with deferred traffic */ @@ -505,6 +677,12 @@ static int iwl_mvm_reserve_sta_stream(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); int queue; + /* + * Check for inactive queues, so we don't reach a situation where we + * can't add a STA due to a shortage in queues that doesn't really exist + */ + iwl_mvm_inactivity_check(mvm); + spin_lock_bh(&mvm->queue_info_lock); /* Make sure we have free resources for this STA */ @@ -514,7 +692,8 @@ static int iwl_mvm_reserve_sta_stream(struct iwl_mvm *mvm, IWL_MVM_QUEUE_FREE)) queue = IWL_MVM_DQA_BSS_CLIENT_QUEUE; else - queue = iwl_mvm_find_free_queue(mvm, IWL_MVM_DQA_MIN_DATA_QUEUE, + queue = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id, + IWL_MVM_DQA_MIN_DATA_QUEUE, IWL_MVM_DQA_MAX_DATA_QUEUE); if (queue < 0) { spin_unlock_bh(&mvm->queue_info_lock); @@ -1403,8 +1582,8 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, return ret; } -static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - int tid, u8 queue, bool start) +int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + int tid, u8 queue, bool start) { struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_add_sta_cmd cmd = {}; @@ -1459,6 +1638,7 @@ const u8 tid_to_mac80211_ac[] = { IEEE80211_AC_VI, IEEE80211_AC_VO, IEEE80211_AC_VO, + IEEE80211_AC_VO, /* We treat MGMT as TID 8, which is set as AC_VO */ }; static const u8 tid_to_ucode_ac[] = { @@ -1513,7 +1693,8 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, txq_id = mvmsta->tid_data[tid].txq_id; if (!iwl_mvm_is_dqa_supported(mvm) || mvm->queue_info[txq_id].status != IWL_MVM_QUEUE_READY) { - txq_id = iwl_mvm_find_free_queue(mvm, mvm->first_agg_queue, + txq_id = iwl_mvm_find_free_queue(mvm, mvmsta->sta_id, + mvm->first_agg_queue, mvm->last_agg_queue); if (txq_id < 0) { ret = txq_id; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index d2c58f134fcf..1588eb63b7ec 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -321,6 +321,9 @@ enum iwl_mvm_agg_state { * Basically when next_reclaimed reaches ssn, we can tell mac80211 that * we are ready to finish the Tx AGG stop / start flow. * @tx_time: medium time consumed by this A-MPDU + * @is_tid_active: has this TID sent traffic in the last + * %IWL_MVM_DQA_QUEUE_TIMEOUT time period. If %txq_id is invalid, this + * field should be ignored. */ struct iwl_mvm_tid_data { struct sk_buff_head deferred_tx_frames; @@ -333,6 +336,7 @@ struct iwl_mvm_tid_data { u16 txq_id; u16 ssn; u16 tx_time; + bool is_tid_active; }; static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data) @@ -509,6 +513,9 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid); +int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + int tid, u8 queue, bool start); + int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm); void iwl_mvm_del_aux_sta(struct iwl_mvm *mvm); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 779bafcbc9a1..9943013dec98 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -884,9 +884,11 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, * nullfunc frames should go to the MGMT queue regardless of QOS */ tid = IWL_MAX_TID_COUNT; - txq_id = mvmsta->tid_data[tid].txq_id; } + if (iwl_mvm_is_dqa_supported(mvm)) + txq_id = mvmsta->tid_data[tid].txq_id; + /* Copy MAC header from skb into command buffer */ memcpy(tx_cmd->hdr, hdr, hdrlen); @@ -905,9 +907,12 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, txq_id = mvmsta->tid_data[tid].txq_id; } - if (iwl_mvm_is_dqa_supported(mvm)) { - if (unlikely(mvmsta->tid_data[tid].txq_id == - IEEE80211_INVAL_HW_QUEUE)) { + /* Check if TXQ needs to be allocated or re-activated */ + if (unlikely(txq_id == IEEE80211_INVAL_HW_QUEUE || + !mvmsta->tid_data[tid].is_tid_active) && + iwl_mvm_is_dqa_supported(mvm)) { + /* If TXQ needs to be allocated... */ + if (txq_id == IEEE80211_INVAL_HW_QUEUE) { iwl_mvm_tx_add_stream(mvm, mvmsta, tid, skb); /* @@ -917,11 +922,22 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, iwl_trans_free_tx_cmd(mvm->trans, dev_cmd); spin_unlock(&mvmsta->lock); return 0; + } - txq_id = mvmsta->tid_data[tid].txq_id; + /* If we are here - TXQ exists and needs to be re-activated */ + spin_lock(&mvm->queue_info_lock); + mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_READY; + mvmsta->tid_data[tid].is_tid_active = true; + spin_unlock(&mvm->queue_info_lock); + + IWL_DEBUG_TX_QUEUES(mvm, "Re-activating queue %d for TX\n", + txq_id); } + /* Keep track of the time of the last frame for this RA/TID */ + mvm->queue_info[txq_id].last_frame_time[tid] = jiffies; + IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id, tid, txq_id, IEEE80211_SEQ_TO_SN(seq_number)); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 161b99efd63d..a0cb5ca4c9b9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -579,17 +579,29 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) iwl_mvm_dump_umac_error_log(mvm); } -int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq) +int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id, u8 minq, u8 maxq) { int i; lockdep_assert_held(&mvm->queue_info_lock); + /* Start by looking for a free queue */ for (i = minq; i <= maxq; i++) if (mvm->queue_info[i].hw_queue_refcount == 0 && mvm->queue_info[i].status == IWL_MVM_QUEUE_FREE) return i; + /* + * If no free queue found - settle for an inactive one to reconfigure + * Make sure that the inactive queue either already belongs to this STA, + * or that if it belongs to another one - it isn't the reserved queue + */ + for (i = minq; i <= maxq; i++) + if (mvm->queue_info[i].status == IWL_MVM_QUEUE_INACTIVE && + (sta_id == mvm->queue_info[i].ra_sta_id || + !mvm->queue_info[i].reserved)) + return i; + return -ENOSPC; } @@ -650,6 +662,7 @@ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, else mvm->queue_info[queue].ra_sta_id = cfg->sta_id; mvm->queue_info[queue].tid_bitmap |= BIT(cfg->tid); + mvm->queue_info[queue].ra_sta_id = cfg->sta_id; IWL_DEBUG_TX_QUEUES(mvm, "Enabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n", @@ -752,6 +765,9 @@ void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, mvm->queue_info[queue].tid_bitmap = 0; mvm->queue_info[queue].hw_queue_to_mac80211 = 0; + /* Regardless if this is a reserved TXQ for a STA - mark it as false */ + mvm->queue_info[queue].reserved = false; + spin_unlock_bh(&mvm->queue_info_lock); iwl_trans_txq_disable(mvm->trans, queue, false); @@ -1039,6 +1055,154 @@ void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif, ieee80211_connection_loss(vif); } +/* + * Remove inactive TIDs of a given queue. + * If all queue TIDs are inactive - mark the queue as inactive + * If only some the queue TIDs are inactive - unmap them from the queue + */ +static void iwl_mvm_remove_inactive_tids(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvmsta, int queue, + unsigned long tid_bitmap) +{ + int tid; + + lockdep_assert_held(&mvmsta->lock); + lockdep_assert_held(&mvm->queue_info_lock); + + /* Go over all non-active TIDs, incl. IWL_MAX_TID_COUNT (for mgmt) */ + for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { + /* If some TFDs are still queued - don't mark TID as inactive */ + if (iwl_mvm_tid_queued(&mvmsta->tid_data[tid])) + tid_bitmap &= ~BIT(tid); + } + + /* If all TIDs in the queue are inactive - mark queue as inactive. */ + if (tid_bitmap == mvm->queue_info[queue].tid_bitmap) { + mvm->queue_info[queue].status = IWL_MVM_QUEUE_INACTIVE; + + for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) + mvmsta->tid_data[tid].is_tid_active = false; + + IWL_DEBUG_TX_QUEUES(mvm, "Queue %d marked as inactive\n", + queue); + return; + } + + /* + * If we are here, this is a shared queue and not all TIDs timed-out. + * Remove the ones that did. + */ + for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { + int mac_queue = mvmsta->vif->hw_queue[tid_to_mac80211_ac[tid]]; + + mvmsta->tid_data[tid].txq_id = IEEE80211_INVAL_HW_QUEUE; + mvm->queue_info[queue].hw_queue_to_mac80211 &= ~BIT(mac_queue); + mvm->queue_info[queue].hw_queue_refcount--; + mvm->queue_info[queue].tid_bitmap &= ~BIT(tid); + mvmsta->tid_data[tid].is_tid_active = false; + + IWL_DEBUG_TX_QUEUES(mvm, + "Removing inactive TID %d from shared Q:%d\n", + tid, queue); + } + + IWL_DEBUG_TX_QUEUES(mvm, + "TXQ #%d left with tid bitmap 0x%x\n", queue, + mvm->queue_info[queue].tid_bitmap); + + /* + * There may be different TIDs with the same mac queues, so make + * sure all TIDs have existing corresponding mac queues enabled + */ + tid_bitmap = mvm->queue_info[queue].tid_bitmap; + for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { + mvm->queue_info[queue].hw_queue_to_mac80211 |= + BIT(mvmsta->vif->hw_queue[tid_to_mac80211_ac[tid]]); + } + + /* TODO: if queue was shared - need to re-enable AGGs */ +} + +void iwl_mvm_inactivity_check(struct iwl_mvm *mvm) +{ + unsigned long timeout_queues_map = 0; + unsigned long now = jiffies; + int i; + + spin_lock_bh(&mvm->queue_info_lock); + for (i = 0; i < IWL_MAX_HW_QUEUES; i++) + if (mvm->queue_info[i].hw_queue_refcount > 0) + timeout_queues_map |= BIT(i); + spin_unlock_bh(&mvm->queue_info_lock); + + rcu_read_lock(); + + /* + * If a queue time outs - mark it as INACTIVE (don't remove right away + * if we don't have to.) This is an optimization in case traffic comes + * later, and we don't HAVE to use a currently-inactive queue + */ + for_each_set_bit(i, &timeout_queues_map, IWL_MAX_HW_QUEUES) { + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + u8 sta_id; + int tid; + unsigned long inactive_tid_bitmap = 0; + unsigned long queue_tid_bitmap; + + spin_lock_bh(&mvm->queue_info_lock); + queue_tid_bitmap = mvm->queue_info[i].tid_bitmap; + + /* If TXQ isn't in active use anyway - nothing to do here... */ + if (mvm->queue_info[i].status != IWL_MVM_QUEUE_READY) { + spin_unlock_bh(&mvm->queue_info_lock); + continue; + } + + /* Check to see if there are inactive TIDs on this queue */ + for_each_set_bit(tid, &queue_tid_bitmap, + IWL_MAX_TID_COUNT + 1) { + if (time_after(mvm->queue_info[i].last_frame_time[tid] + + IWL_MVM_DQA_QUEUE_TIMEOUT, now)) + continue; + + inactive_tid_bitmap |= BIT(tid); + } + spin_unlock_bh(&mvm->queue_info_lock); + + /* If all TIDs are active - finish check on this queue */ + if (!inactive_tid_bitmap) + continue; + + /* + * If we are here - the queue hadn't been served recently and is + * in use + */ + + sta_id = mvm->queue_info[i].ra_sta_id; + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + + /* + * If the STA doesn't exist anymore, it isn't an error. It could + * be that it was removed since getting the queues, and in this + * case it should've inactivated its queues anyway. + */ + if (IS_ERR_OR_NULL(sta)) + continue; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + + spin_lock_bh(&mvmsta->lock); + spin_lock(&mvm->queue_info_lock); + iwl_mvm_remove_inactive_tids(mvm, mvmsta, i, + inactive_tid_bitmap); + spin_unlock(&mvm->queue_info_lock); + spin_unlock_bh(&mvmsta->lock); + } + + rcu_read_unlock(); +} + int iwl_mvm_send_lqm_cmd(struct ieee80211_vif *vif, enum iwl_lqm_cmd_operatrions operation, u32 duration, u32 timeout) From 2047fa5401918a1e3bc0ec8923de2570265d1d78 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Sun, 1 May 2016 11:40:49 +0300 Subject: [PATCH 04/56] iwlwifi: pcie: unify restock calls on init Currently code calls restock for mq devices during the init function, unlike sq where restock is called after init. This causes an harmless but alarming deadlock warning from lockdep, to fix this - unify the init code. Rename the restock functions while at it. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 28 +++++++++----------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 70e39e4ec0c0..8d4a60dcb32a 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -233,10 +233,10 @@ static void iwl_pcie_rxq_check_wrptr(struct iwl_trans *trans) } /* - * iwl_pcie_rxq_mq_restock - restock implementation for multi-queue rx + * iwl_pcie_rxmq_restock - restock implementation for multi-queue rx */ -static void iwl_pcie_rxq_mq_restock(struct iwl_trans *trans, - struct iwl_rxq *rxq) +static void iwl_pcie_rxmq_restock(struct iwl_trans *trans, + struct iwl_rxq *rxq) { struct iwl_rx_mem_buffer *rxb; @@ -281,10 +281,10 @@ static void iwl_pcie_rxq_mq_restock(struct iwl_trans *trans, } /* - * iwl_pcie_rxq_sq_restock - restock implementation for single queue rx + * iwl_pcie_rxsq_restock - restock implementation for single queue rx */ -static void iwl_pcie_rxq_sq_restock(struct iwl_trans *trans, - struct iwl_rxq *rxq) +static void iwl_pcie_rxsq_restock(struct iwl_trans *trans, + struct iwl_rxq *rxq) { struct iwl_rx_mem_buffer *rxb; @@ -343,9 +343,9 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans, struct iwl_rxq *rxq) { if (trans->cfg->mq_rx_supported) - iwl_pcie_rxq_mq_restock(trans, rxq); + iwl_pcie_rxmq_restock(trans, rxq); else - iwl_pcie_rxq_sq_restock(trans, rxq); + iwl_pcie_rxsq_restock(trans, rxq); } /* @@ -828,9 +828,6 @@ static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans) enabled |= BIT(i) | BIT(i + 16); } - /* restock default queue */ - iwl_pcie_rxq_mq_restock(trans, &trans_pcie->rxq[0]); - /* * Enable Rx DMA * Rx buffer size 4 or 8k or 12k @@ -960,12 +957,13 @@ int iwl_pcie_rx_init(struct iwl_trans *trans) } iwl_pcie_rxq_alloc_rbs(trans, GFP_KERNEL, def_rxq); - if (trans->cfg->mq_rx_supported) { + + if (trans->cfg->mq_rx_supported) iwl_pcie_rx_mq_hw_init(trans); - } else { - iwl_pcie_rxq_sq_restock(trans, def_rxq); + else iwl_pcie_rx_hw_init(trans, def_rxq); - } + + iwl_pcie_rxq_restock(trans, def_rxq); spin_lock(&def_rxq->lock); iwl_pcie_rxq_inc_wr_ptr(trans, def_rxq); From f43495fd5e37b2ef4a187974011ff439e6354053 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 4 May 2016 14:22:10 +0300 Subject: [PATCH 05/56] iwlwifi: mvm: fix possible division by zero Theoretically we may get only one IRQ from OS, in which case we will have only 1 queue even in MSIx mode. This will cause division by zero in the indirection table calculation. We do not need send the command in that case, as there is only one queue so all RX traffic will be directed to it anyway. Bail out early if there is only one queue. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 7057f35cb2e7..0a5490c46d2e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -122,6 +122,9 @@ static int iwl_send_rss_cfg_cmd(struct iwl_mvm *mvm) IWL_RSS_HASH_TYPE_IPV6_PAYLOAD, }; + if (mvm->trans->num_rx_queues == 1) + return 0; + /* Do not direct RSS traffic to Q 0 which is our fallback queue */ for (i = 0; i < ARRAY_SIZE(cmd.indirection_table); i++) cmd.indirection_table[i] = From 69e046423ad71de625ac1b0f0f390d3b9727b8c9 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 3 May 2016 12:18:33 +0300 Subject: [PATCH 06/56] iwlwifi: mvm: change scan timeout to a delayed work Some transports may sleep when writing to registers, which is done when calling iwl_force_nmi(). So we can't call iwl_force_nmi() in a timer context. To solve that, convert the scan timeout timer to a delayed work. Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 1 + drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 6 +----- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 18 ++++++++++-------- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index e5cb7dbcd05c..af1d266d6fe8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1200,6 +1200,7 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) flush_work(&mvm->add_stream_wk); cancel_delayed_work_sync(&mvm->fw_dump_wk); cancel_delayed_work_sync(&mvm->cs_tx_unblock_dwork); + cancel_delayed_work_sync(&mvm->scan_timeout_dwork); iwl_mvm_free_fw_dump_desc(mvm); mutex_lock(&mvm->mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index bf7d78e0e14a..3775e26b7d9d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -802,7 +802,7 @@ struct iwl_mvm { struct iwl_mcast_filter_cmd *mcast_filter_cmd; enum iwl_mvm_scan_type scan_type; enum iwl_mvm_sched_scan_pass_all_states sched_scan_pass_all; - struct timer_list scan_timer; + struct delayed_work scan_timeout_dwork; /* max number of simultaneous scans the FW supports */ unsigned int max_scans; @@ -1415,7 +1415,7 @@ int iwl_mvm_scan_size(struct iwl_mvm *mvm); int iwl_mvm_scan_stop(struct iwl_mvm *mvm, int type, bool notify); int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm); void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm); -void iwl_mvm_scan_timeout(unsigned long data); +void iwl_mvm_scan_timeout_wk(struct work_struct *work); /* Scheduled scan */ void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 632b1dc552a0..063ebbeb8481 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -608,6 +608,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work); INIT_DELAYED_WORK(&mvm->fw_dump_wk, iwl_mvm_fw_error_dump_wk); INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work); + INIT_DELAYED_WORK(&mvm->scan_timeout_dwork, iwl_mvm_scan_timeout_wk); INIT_WORK(&mvm->add_stream_wk, iwl_mvm_add_new_dqa_stream_wk); spin_lock_init(&mvm->d0i3_tx_lock); @@ -766,9 +767,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_mvm_tof_init(mvm); - setup_timer(&mvm->scan_timer, iwl_mvm_scan_timeout, - (unsigned long)mvm); - return op_mode; out_unregister: @@ -822,8 +820,6 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) iwl_mvm_tof_clean(mvm); - del_timer_sync(&mvm->scan_timer); - mutex_destroy(&mvm->mutex); mutex_destroy(&mvm->d0i3_suspend_mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 6f609dd5c222..fb25d9e41912 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -399,7 +399,7 @@ void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm, ieee80211_scan_completed(mvm->hw, scan_notif->status == IWL_SCAN_OFFLOAD_ABORTED); iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); - del_timer(&mvm->scan_timer); + cancel_delayed_work(&mvm->scan_timeout_dwork); } else { IWL_ERR(mvm, "got scan complete notification but no scan is running\n"); @@ -1222,15 +1222,16 @@ static int iwl_mvm_check_running_scans(struct iwl_mvm *mvm, int type) return -EIO; } -#define SCAN_TIMEOUT (16 * HZ) +#define SCAN_TIMEOUT 20000 -void iwl_mvm_scan_timeout(unsigned long data) +void iwl_mvm_scan_timeout_wk(struct work_struct *work) { - struct iwl_mvm *mvm = (struct iwl_mvm *)data; + struct delayed_work *delayed_work = to_delayed_work(work); + struct iwl_mvm *mvm = container_of(delayed_work, struct iwl_mvm, + scan_timeout_dwork); IWL_ERR(mvm, "regular scan timed out\n"); - del_timer(&mvm->scan_timer); iwl_force_nmi(mvm->trans); } @@ -1313,7 +1314,8 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, mvm->scan_status |= IWL_MVM_SCAN_REGULAR; iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN); - mod_timer(&mvm->scan_timer, jiffies + SCAN_TIMEOUT); + queue_delayed_work(system_wq, &mvm->scan_timeout_dwork, + msecs_to_jiffies(SCAN_TIMEOUT)); return 0; } @@ -1432,7 +1434,7 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_REGULAR) { ieee80211_scan_completed(mvm->hw, aborted); iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); - del_timer(&mvm->scan_timer); + cancel_delayed_work(&mvm->scan_timeout_dwork); } 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; @@ -1628,7 +1630,7 @@ int iwl_mvm_scan_stop(struct iwl_mvm *mvm, int type, bool notify) * to release the scan reference here. */ iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); - del_timer(&mvm->scan_timer); + cancel_delayed_work(&mvm->scan_timeout_dwork); if (notify) ieee80211_scan_completed(mvm->hw, true); } else if (notify) { From bdbc58abe7960ec1c87bd6201d31538fbda0d1ba Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 4 May 2016 09:19:13 +0300 Subject: [PATCH 07/56] iwlwifi: mvm: remove an unused variable We never initialize ampdu_status so it causes a static checker warning when we pass it to iwl_mvm_pass_packet_to_mac80211(). Fortunately, it's never used so we can just remove it. Signed-off-by: Dan Carpenter Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 6d096b6c3d50..2d5a7cc771d2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -101,7 +101,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, struct napi_struct *napi, struct sk_buff *skb, struct ieee80211_hdr *hdr, u16 len, - u32 ampdu_status, u8 crypt_len, + u8 crypt_len, struct iwl_rx_cmd_buffer *rxb) { unsigned int hdrlen, fraglen; @@ -268,7 +268,6 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, struct ieee80211_sta *sta = NULL; struct sk_buff *skb; u32 len; - u32 ampdu_status; u32 rate_n_flags; u32 rx_pkt_status; u8 crypt_len = 0; @@ -480,7 +479,7 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, iwl_mvm_ref(mvm, IWL_MVM_REF_RX); iwl_mvm_pass_packet_to_mac80211(mvm, sta, napi, skb, hdr, len, - ampdu_status, crypt_len, rxb); + crypt_len, rxb); if (take_ref) iwl_mvm_unref(mvm, IWL_MVM_REF_RX); From 32afd15b0f56eb9adf107fb52c6b76c308f5966c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 4 May 2016 09:19:54 +0300 Subject: [PATCH 08/56] iwlwifi: mvm: silence uninitialized variable warning "max_amsdu_len" isn't set if kstrtouint() fails. Signed-off-by: Dan Carpenter Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 406cf1cb945c..b34489817c70 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -1020,6 +1020,8 @@ static ssize_t iwl_dbgfs_max_amsdu_len_write(struct iwl_mvm *mvm, int ret; ret = kstrtouint(buf, 0, &max_amsdu_len); + if (ret) + return ret; if (max_amsdu_len > IEEE80211_MAX_MPDU_LEN_VHT_11454) return -EINVAL; From 42db09c1b0378e118b804d948a5bab6194721506 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Mon, 2 May 2016 14:01:14 +0300 Subject: [PATCH 09/56] iwlwifi: mvm: support dqa queue sharing Support DQA queue sharing when no free queue exists for allocation to a STA that already exists. This means that a single queue will serve more than a single TID (although the RA will be the same for all TIDs served). We try to choose the lowest AC possible, to ensure the shared queues have the lowest possible combined AC requirements. The queue to share is chosen only from the same RA's DATA queues as follows (in descending priority): 1. An AC_BE queue 2. Same AC queue 3. Highest AC queue that is lower than new AC 4. Any existing AC (there always is at least 1 DATA queue) If any aggregations existed for any of the TIDs of the shared queue - they are stopped (the FW is notified), but no delBA is sent. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/iwl-trans.h | 11 ++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 6 + drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 173 ++++++++++++++++-- .../net/wireless/intel/iwlwifi/mvm/utils.c | 18 +- .../wireless/intel/iwlwifi/pcie/internal.h | 2 + .../net/wireless/intel/iwlwifi/pcie/trans.c | 2 + drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 9 + 7 files changed, 199 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index a45be1db179b..57cc67f092ea 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -583,6 +583,7 @@ struct iwl_trans_txq_scd_cfg { * configured. May sleep. * @txq_disable: de-configure a Tx queue to send AMPDUs * Must be atomic + * @txq_set_shared_mode: change Tx queue shared/unshared marking * @wait_tx_queue_empty: wait until tx queues are empty. May sleep. * @freeze_txq_timer: prevents the timer of the queue from firing until the * queue is set to awake. Must be atomic. @@ -646,6 +647,9 @@ struct iwl_trans_ops { void (*txq_disable)(struct iwl_trans *trans, int queue, bool configure_scd); + void (*txq_set_shared_mode)(struct iwl_trans *trans, u32 txq_id, + bool shared); + int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm); void (*freeze_txq_timer)(struct iwl_trans *trans, unsigned long txqs, bool freeze); @@ -1061,6 +1065,13 @@ iwl_trans_txq_enable_cfg(struct iwl_trans *trans, int queue, u16 ssn, trans->ops->txq_enable(trans, queue, ssn, cfg, queue_wdg_timeout); } +static inline void iwl_trans_txq_set_shared_mode(struct iwl_trans *trans, + int queue, bool shared_mode) +{ + if (trans->ops->txq_set_shared_mode) + trans->ops->txq_set_shared_mode(trans, queue, shared_mode); +} + static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue, int fifo, int sta_id, int tid, int frame_limit, u16 ssn, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 3775e26b7d9d..6092af23969f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -687,6 +687,10 @@ struct iwl_mvm_baid_data { * This is the state of a queue that has been fully configured (including * SCD pointers, etc), has a specific RA/TID assigned to it, and can be * used to send traffic. + * @IWL_MVM_QUEUE_SHARED: queue is shared, or in a process of becoming shared + * This is a state in which a single queue serves more than one TID, all of + * which are not aggregated. Note that the queue is only associated to one + * RA. * @IWL_MVM_QUEUE_INACTIVE: queue is allocated but no traffic on it * This is a state of a queue that has had traffic on it, but during the * last %IWL_MVM_DQA_QUEUE_TIMEOUT time period there has been no traffic on @@ -698,6 +702,7 @@ enum iwl_mvm_queue_status { IWL_MVM_QUEUE_FREE, IWL_MVM_QUEUE_RESERVED, IWL_MVM_QUEUE_READY, + IWL_MVM_QUEUE_SHARED, IWL_MVM_QUEUE_INACTIVE, }; @@ -760,6 +765,7 @@ struct iwl_mvm { u8 hw_queue_refcount; u8 ra_sta_id; /* The RA this queue is mapped to, if exists */ bool reserved; /* Is this the TXQ reserved for a STA */ + u8 mac80211_ac; /* The mac80211 AC this queue is mapped to */ u16 tid_bitmap; /* Bitmap of the TIDs mapped to this queue */ /* Timestamp for inactivation per TID of this queue */ unsigned long last_frame_time[IWL_MAX_TID_COUNT + 1]; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 84384a4321d9..14c06cbe861f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -361,6 +361,40 @@ static int iwl_mvm_invalidate_sta_queue(struct iwl_mvm *mvm, int queue, return ret; } +static int iwl_mvm_get_queue_agg_tids(struct iwl_mvm *mvm, int queue) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + unsigned long tid_bitmap; + unsigned long agg_tids = 0; + s8 sta_id; + int tid; + + lockdep_assert_held(&mvm->mutex); + + spin_lock_bh(&mvm->queue_info_lock); + sta_id = mvm->queue_info[queue].ra_sta_id; + tid_bitmap = mvm->queue_info[queue].tid_bitmap; + spin_unlock_bh(&mvm->queue_info_lock); + + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], + lockdep_is_held(&mvm->mutex)); + + if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) + return -EINVAL; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + + spin_lock_bh(&mvmsta->lock); + for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { + if (mvmsta->tid_data[tid].state == IWL_AGG_ON) + agg_tids |= BIT(tid); + } + spin_unlock_bh(&mvmsta->lock); + + return agg_tids; +} + /* * Remove a queue from a station's resources. * Note that this only marks as free. It DOESN'T delete a BA agreement, and @@ -394,28 +428,91 @@ static int iwl_mvm_remove_sta_queue_marking(struct iwl_mvm *mvm, int queue) mvmsta = iwl_mvm_sta_from_mac80211(sta); spin_lock_bh(&mvmsta->lock); + /* Unmap MAC queues and TIDs from this queue */ for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { - mvmsta->tid_data[tid].txq_id = IEEE80211_INVAL_HW_QUEUE; - if (mvmsta->tid_data[tid].state == IWL_AGG_ON) disable_agg_tids |= BIT(tid); + mvmsta->tid_data[tid].txq_id = IEEE80211_INVAL_HW_QUEUE; } - mvmsta->tfd_queue_msk &= ~BIT(queue); /* Don't use this queue anymore */ + mvmsta->tfd_queue_msk &= ~BIT(queue); /* Don't use this queue anymore */ spin_unlock_bh(&mvmsta->lock); rcu_read_unlock(); - spin_lock(&mvm->queue_info_lock); + spin_lock_bh(&mvm->queue_info_lock); /* Unmap MAC queues and TIDs from this queue */ mvm->queue_info[queue].hw_queue_to_mac80211 = 0; mvm->queue_info[queue].hw_queue_refcount = 0; mvm->queue_info[queue].tid_bitmap = 0; - spin_unlock(&mvm->queue_info_lock); + spin_unlock_bh(&mvm->queue_info_lock); return disable_agg_tids; } +static int iwl_mvm_get_shared_queue(struct iwl_mvm *mvm, + unsigned long tfd_queue_mask, u8 ac) +{ + int queue = 0; + u8 ac_to_queue[IEEE80211_NUM_ACS]; + int i; + + lockdep_assert_held(&mvm->queue_info_lock); + + memset(&ac_to_queue, IEEE80211_INVAL_HW_QUEUE, sizeof(ac_to_queue)); + + /* See what ACs the existing queues for this STA have */ + for_each_set_bit(i, &tfd_queue_mask, IWL_MVM_DQA_MAX_DATA_QUEUE) { + /* Only DATA queues can be shared */ + if (i < IWL_MVM_DQA_MIN_DATA_QUEUE && + i != IWL_MVM_DQA_BSS_CLIENT_QUEUE) + continue; + + ac_to_queue[mvm->queue_info[i].mac80211_ac] = i; + } + + /* + * The queue to share is chosen only from DATA queues as follows (in + * descending priority): + * 1. An AC_BE queue + * 2. Same AC queue + * 3. Highest AC queue that is lower than new AC + * 4. Any existing AC (there always is at least 1 DATA queue) + */ + + /* Priority 1: An AC_BE queue */ + if (ac_to_queue[IEEE80211_AC_BE] != IEEE80211_INVAL_HW_QUEUE) + queue = ac_to_queue[IEEE80211_AC_BE]; + /* Priority 2: Same AC queue */ + else if (ac_to_queue[ac] != IEEE80211_INVAL_HW_QUEUE) + queue = ac_to_queue[ac]; + /* Priority 3a: If new AC is VO and VI exists - use VI */ + else if (ac == IEEE80211_AC_VO && + ac_to_queue[IEEE80211_AC_VI] != IEEE80211_INVAL_HW_QUEUE) + queue = ac_to_queue[IEEE80211_AC_VI]; + /* Priority 3b: No BE so only AC less than the new one is BK */ + else if (ac_to_queue[IEEE80211_AC_BK] != IEEE80211_INVAL_HW_QUEUE) + queue = ac_to_queue[IEEE80211_AC_BK]; + /* Priority 4a: No BE nor BK - use VI if exists */ + else if (ac_to_queue[IEEE80211_AC_VI] != IEEE80211_INVAL_HW_QUEUE) + queue = ac_to_queue[IEEE80211_AC_VI]; + /* Priority 4b: No BE, BK nor VI - use VO if exists */ + else if (ac_to_queue[IEEE80211_AC_VO] != IEEE80211_INVAL_HW_QUEUE) + queue = ac_to_queue[IEEE80211_AC_VO]; + + /* Make sure queue found (or not) is legal */ + if (!((queue >= IWL_MVM_DQA_MIN_MGMT_QUEUE && + queue <= IWL_MVM_DQA_MAX_MGMT_QUEUE) || + (queue >= IWL_MVM_DQA_MIN_DATA_QUEUE && + queue <= IWL_MVM_DQA_MAX_DATA_QUEUE) || + (queue == IWL_MVM_DQA_BSS_CLIENT_QUEUE))) { + IWL_ERR(mvm, "No DATA queues available to share\n"); + queue = -ENOSPC; + } + + return queue; +} + static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, struct ieee80211_sta *sta, u8 ac, int tid, struct ieee80211_hdr *hdr) @@ -434,11 +531,17 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, bool using_inactive_queue = false; unsigned long disable_agg_tids = 0; enum iwl_mvm_agg_state queue_state; + bool shared_queue = false; int ssn; + unsigned long tfd_queue_mask; int ret; lockdep_assert_held(&mvm->mutex); + spin_lock_bh(&mvmsta->lock); + tfd_queue_mask = mvmsta->tfd_queue_msk; + spin_unlock_bh(&mvmsta->lock); + spin_lock_bh(&mvm->queue_info_lock); /* @@ -487,20 +590,32 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, queue, mvmsta->sta_id, tid); } + /* No free queue - we'll have to share */ + if (queue <= 0) { + queue = iwl_mvm_get_shared_queue(mvm, tfd_queue_mask, ac); + if (queue > 0) { + shared_queue = true; + mvm->queue_info[queue].status = IWL_MVM_QUEUE_SHARED; + } + } + /* * Mark TXQ as ready, even though it hasn't been fully configured yet, * to make sure no one else takes it. * This will allow avoiding re-acquiring the lock at the end of the * configuration. On error we'll mark it back as free. */ - if (queue >= 0) + if ((queue > 0) && !shared_queue) mvm->queue_info[queue].status = IWL_MVM_QUEUE_READY; spin_unlock_bh(&mvm->queue_info_lock); - /* TODO: support shared queues for same RA */ - if (queue < 0) + /* This shouldn't happen - out of queues */ + if (WARN_ON(queue <= 0)) { + IWL_ERR(mvm, "No available queues for tid %d on sta_id %d\n", + tid, cfg.sta_id); return -ENOSPC; + } /* * Actual en/disablement of aggregations is through the ADD_STA HCMD, @@ -543,8 +658,27 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, } } - IWL_DEBUG_TX_QUEUES(mvm, "Allocating queue #%d to sta %d on tid %d\n", - queue, mvmsta->sta_id, tid); + IWL_DEBUG_TX_QUEUES(mvm, + "Allocating %squeue #%d to sta %d on tid %d\n", + shared_queue ? "shared " : "", queue, + mvmsta->sta_id, tid); + + if (shared_queue) { + /* Disable any open aggs on this queue */ + disable_agg_tids = iwl_mvm_get_queue_agg_tids(mvm, queue); + + if (disable_agg_tids) { + IWL_DEBUG_TX_QUEUES(mvm, "Disabling aggs on queue %d\n", + queue); + iwl_mvm_invalidate_sta_queue(mvm, queue, + disable_agg_tids, false); + } + + /* Mark queue as shared in transport */ + iwl_trans_txq_set_shared_mode(mvm->trans, queue, true); + + /* TODO: a redirection may be required - DQA phase 2 */ + } ssn = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); iwl_mvm_enable_txq(mvm, queue, mac_queue, ssn, &cfg, @@ -560,15 +694,20 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, mvmsta->reserved_queue = IEEE80211_INVAL_HW_QUEUE; spin_unlock_bh(&mvmsta->lock); - ret = iwl_mvm_sta_send_to_fw(mvm, sta, true, STA_MODIFY_QUEUES); - if (ret) - goto out_err; + if (!shared_queue) { + ret = iwl_mvm_sta_send_to_fw(mvm, sta, true, STA_MODIFY_QUEUES); + if (ret) + goto out_err; - /* If we need to re-enable aggregations... */ - if (queue_state == IWL_AGG_ON) - ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true); + /* If we need to re-enable aggregations... */ + if (queue_state == IWL_AGG_ON) { + ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true); + if (ret) + goto out_err; + } + } - return ret; + return 0; out_err: iwl_mvm_disable_txq(mvm, queue, mac_queue, tid, 0); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index a0cb5ca4c9b9..2fc51e7c03b5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -655,15 +655,22 @@ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, } /* Update mappings and refcounts */ + if (mvm->queue_info[queue].hw_queue_refcount > 0) + enable_queue = false; + mvm->queue_info[queue].hw_queue_to_mac80211 |= BIT(mac80211_queue); mvm->queue_info[queue].hw_queue_refcount++; - if (mvm->queue_info[queue].hw_queue_refcount > 1) - enable_queue = false; - else - mvm->queue_info[queue].ra_sta_id = cfg->sta_id; mvm->queue_info[queue].tid_bitmap |= BIT(cfg->tid); mvm->queue_info[queue].ra_sta_id = cfg->sta_id; + if (enable_queue) { + if (cfg->tid != IWL_MAX_TID_COUNT) + mvm->queue_info[queue].mac80211_ac = + tid_to_mac80211_ac[cfg->tid]; + else + mvm->queue_info[queue].mac80211_ac = IEEE80211_AC_VO; + } + IWL_DEBUG_TX_QUEUES(mvm, "Enabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n", queue, mvm->queue_info[queue].hw_queue_refcount, @@ -1154,7 +1161,8 @@ void iwl_mvm_inactivity_check(struct iwl_mvm *mvm) queue_tid_bitmap = mvm->queue_info[i].tid_bitmap; /* If TXQ isn't in active use anyway - nothing to do here... */ - if (mvm->queue_info[i].status != IWL_MVM_QUEUE_READY) { + if (mvm->queue_info[i].status != IWL_MVM_QUEUE_READY && + mvm->queue_info[i].status != IWL_MVM_QUEUE_SHARED) { spin_unlock_bh(&mvm->queue_info_lock); continue; } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 482836ee94f2..8bfa9159d27d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -471,6 +471,8 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int queue, u16 ssn, unsigned int wdg_timeout); void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue, bool configure_scd); +void iwl_trans_pcie_txq_set_shared_mode(struct iwl_trans *trans, u32 txq_id, + bool shared_mode); int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int txq_id); void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 33fd2175e046..3badebb0febf 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2745,6 +2745,8 @@ static const struct iwl_trans_ops trans_ops_pcie = { .txq_disable = iwl_trans_pcie_txq_disable, .txq_enable = iwl_trans_pcie_txq_enable, + .txq_set_shared_mode = iwl_trans_pcie_txq_set_shared_mode, + .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty, .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer, .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index d6beac9af029..8901d49aebd5 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -1354,6 +1354,15 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn, txq->active = true; } +void iwl_trans_pcie_txq_set_shared_mode(struct iwl_trans *trans, u32 txq_id, + bool shared_mode) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + + txq->ampdu = !shared_mode; +} + void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id, bool configure_scd) { From 93f436e2c7feacb04a21bbfb984a7afd87fb4623 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Mon, 31 Aug 2015 13:41:26 +0300 Subject: [PATCH 10/56] iwlwifi: mvm: set sta_id in SCD_QUEUE_CONFIG cmd Set the correct sta_id in the SCD_QUEUE_CONFIG command sent to the FW when enabling/disabling queues. This is needed in DQA-mode to allow the FW to associate between queue and STA. In case the queue isn't connected to a specific station but rather is a static "generic" queue - the sta_id should be set to 0x10 (max supported STA is 0x0f). Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 7 +++++++ drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 14c06cbe861f..9d4ebc9439a0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -635,9 +635,16 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, .scd_queue = queue, .enable = 0, }; + u8 ac; disable_agg_tids = iwl_mvm_remove_sta_queue_marking(mvm, queue); + spin_lock_bh(&mvm->queue_info_lock); + ac = mvm->queue_info[queue].mac80211_ac; + cmd.sta_id = mvm->queue_info[queue].ra_sta_id; + cmd.tx_fifo = iwl_mvm_ac_to_tx_fifo[ac]; + spin_unlock_bh(&mvm->queue_info_lock); + /* Disable the queue */ iwl_mvm_invalidate_sta_queue(mvm, queue, disable_agg_tids, true); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 2fc51e7c03b5..68f4e7fdfc11 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -691,6 +691,10 @@ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, .tid = cfg->tid, }; + /* Set sta_id in the command, if it exists */ + if (iwl_mvm_is_dqa_supported(mvm)) + cmd.sta_id = cfg->sta_id; + iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL, wdg_timeout); WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), From 28d0793ed212e4714cea79aeb77e62de99b139c1 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Tue, 1 Sep 2015 16:36:25 +0300 Subject: [PATCH 11/56] iwlwifi: mvm: update aux queue in dqa mode In DQA mode the AUX queue is mapped elsewhere than in non- DQA mode. Update the code to reflect this. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/mvm/fw-api.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 15 +++++++++------ drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 18 ++++++++++++++++-- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index b06380d8473b..a1d3d95499d6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -90,6 +90,7 @@ enum { * DQA queue numbers * * @IWL_MVM_DQA_CMD_QUEUE: a queue reserved for sending HCMDs to the FW + * @IWL_MVM_DQA_AUX_QUEUE: a queue reserved for aux frames * @IWL_MVM_DQA_P2P_DEVICE_QUEUE: a queue reserved for P2P device frames * @IWL_MVM_DQA_GCAST_QUEUE: a queue reserved for P2P GO/SoftAP GCAST frames * @IWL_MVM_DQA_BSS_CLIENT_QUEUE: a queue reserved for BSS activity, to ensure @@ -108,6 +109,7 @@ enum { */ enum iwl_mvm_dqa_txq { IWL_MVM_DQA_CMD_QUEUE = 0, + IWL_MVM_DQA_AUX_QUEUE = 1, IWL_MVM_DQA_P2P_DEVICE_QUEUE = 2, IWL_MVM_DQA_GCAST_QUEUE = 3, IWL_MVM_DQA_BSS_CLIENT_QUEUE = 4, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 063ebbeb8481..a08db009d32a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -577,18 +577,21 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0; - mvm->aux_queue = 15; if (!iwl_mvm_is_dqa_supported(mvm)) { - mvm->first_agg_queue = 16; mvm->last_agg_queue = mvm->cfg->base_params->num_of_queues - 1; + + if (mvm->cfg->base_params->num_of_queues == 16) { + mvm->aux_queue = 11; + mvm->first_agg_queue = 12; + } else { + mvm->aux_queue = 15; + mvm->first_agg_queue = 16; + } } else { + mvm->aux_queue = IWL_MVM_DQA_AUX_QUEUE; mvm->first_agg_queue = IWL_MVM_DQA_MIN_DATA_QUEUE; mvm->last_agg_queue = IWL_MVM_DQA_MAX_DATA_QUEUE; } - if (mvm->cfg->base_params->num_of_queues == 16) { - mvm->aux_queue = 11; - mvm->first_agg_queue = 12; - } mvm->sf_state = SF_UNINIT; mvm->cur_ucode = IWL_UCODE_INIT; mvm->drop_bcn_ap_mode = true; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 9d4ebc9439a0..cf0681765ec4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1309,8 +1309,9 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm) lockdep_assert_held(&mvm->mutex); /* Map Aux queue to fifo - needs to happen before adding Aux station */ - iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue, mvm->aux_queue, - IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout); + if (!iwl_mvm_is_dqa_supported(mvm)) + iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue, mvm->aux_queue, + IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout); /* Allocate aux station and assign to it the aux queue */ ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, BIT(mvm->aux_queue), @@ -1318,6 +1319,19 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm) if (ret) return ret; + if (iwl_mvm_is_dqa_supported(mvm)) { + struct iwl_trans_txq_scd_cfg cfg = { + .fifo = IWL_MVM_TX_FIFO_MCAST, + .sta_id = mvm->aux_sta.sta_id, + .tid = IWL_MAX_TID_COUNT, + .aggregate = false, + .frame_limit = IWL_FRAME_LIMIT, + }; + + iwl_mvm_enable_txq(mvm, mvm->aux_queue, mvm->aux_queue, 0, &cfg, + wdg_timeout); + } + ret = iwl_mvm_add_int_sta_common(mvm, &mvm->aux_sta, NULL, MAC_INDEX_AUX, 0); From 97d5be7e229426ba17a305a0e51c86e08b89436a Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Mon, 31 Aug 2015 14:33:23 +0300 Subject: [PATCH 12/56] iwlwifi: mvm: support dqa-enable hcmd Support sending the DQA-enablement HCMD to the FW when working in DQA mode. This HCMD will enable DQA-specific flows in the FW. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/mvm/fw-api.h | 9 +++++++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 26 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index a1d3d95499d6..eea03ecafbe0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -332,6 +332,7 @@ enum iwl_system_subcmd_ids { }; enum iwl_data_path_subcmd_ids { + DQA_ENABLE_CMD = 0x0, UPDATE_MU_GROUPS_CMD = 0x1, TRIGGER_RX_QUEUES_NOTIF_CMD = 0x2, MU_GROUP_MGMT_NOTIF = 0xFE, @@ -361,6 +362,14 @@ struct iwl_cmd_response { __le32 status; }; +/* + * struct iwl_dqa_enable_cmd + * @cmd_queue: the TXQ number of the command queue + */ +struct iwl_dqa_enable_cmd { + __le32 cmd_queue; +} __packed; /* DQA_CONTROL_CMD_API_S_VER_1 */ + /* * struct iwl_tx_ant_cfg_cmd * @valid: valid antenna configuration diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 0a5490c46d2e..510b4c1be35d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -134,6 +134,23 @@ static int iwl_send_rss_cfg_cmd(struct iwl_mvm *mvm) return iwl_mvm_send_cmd_pdu(mvm, RSS_CONFIG_CMD, 0, sizeof(cmd), &cmd); } +static int iwl_mvm_send_dqa_cmd(struct iwl_mvm *mvm) +{ + struct iwl_dqa_enable_cmd dqa_cmd = { + .cmd_queue = cpu_to_le32(IWL_MVM_DQA_CMD_QUEUE), + }; + u32 cmd_id = iwl_cmd_id(DQA_ENABLE_CMD, DATA_PATH_GROUP, 0); + int ret; + + ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(dqa_cmd), &dqa_cmd); + if (ret) + IWL_ERR(mvm, "Failed to send DQA enabling command: %d\n", ret); + else + IWL_DEBUG_FW(mvm, "Working in DQA mode\n"); + + return ret; +} + void iwl_free_fw_paging(struct iwl_mvm *mvm) { int i; @@ -979,6 +996,15 @@ int iwl_mvm_up(struct iwl_mvm *mvm) /* reset quota debouncing buffer - 0xff will yield invalid data */ memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd)); + /* Enable DQA-mode if required */ + if (iwl_mvm_is_dqa_supported(mvm)) { + ret = iwl_mvm_send_dqa_cmd(mvm); + if (ret) + goto error; + } else { + IWL_DEBUG_FW(mvm, "Working in non-DQA mode\n"); + } + /* Add auxiliary station for scanning */ ret = iwl_mvm_add_aux_sta(mvm); if (ret) From 4b79deece5d45396422d469afa11f9d69ccb3d8b Mon Sep 17 00:00:00 2001 From: Oren Givon Date: Mon, 23 May 2016 09:58:17 +0300 Subject: [PATCH 13/56] iwlwifi: add new 8260 PCI IDs Add 3 new 8260 series PCI IDs: - (0x24F3, 0x10B0) - (0x24F3, 0xD0B0) - (0x24F3, 0xB0B0) CC: [4.1+] Signed-off-by: Oren Givon Signed-off-by: David Spinadel Signed-off-by: Luca Coelho --- 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 a588b05e38eb..1cae19de18c4 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -433,6 +433,7 @@ static const struct pci_device_id iwl_hw_card_ids[] = { /* 8000 Series */ {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0x1010, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x10B0, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0x0130, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0x1130, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0x0132, iwl8260_2ac_cfg)}, @@ -454,6 +455,8 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x24F3, 0xD010, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0xC050, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0xD050, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xD0B0, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xB0B0, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0x8010, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0x8110, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0x9010, iwl8260_2ac_cfg)}, From f24bbae565d279cd90c904fe55b539a45631705e Mon Sep 17 00:00:00 2001 From: Oren Givon Date: Mon, 23 May 2016 09:58:17 +0300 Subject: [PATCH 14/56] iwlwifi: add new 8265 Add 6 new 8265 series PCI IDs: - (0x24FD, 0x1130) - (0x24FD, 0x0130) - (0x24FD, 0x0910) - (0x24FD, 0x0930) - (0x24FD, 0x0950) - (0x24FD, 0x0850) CC: [4.6+] Signed-off-by: Oren Givon Signed-off-by: David Spinadel Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 1cae19de18c4..6f020e4ec7dc 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -484,6 +484,8 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x24FD, 0x0010, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x0110, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x1110, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x1130, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0130, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x1010, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x0050, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x0150, iwl8265_2ac_cfg)}, @@ -494,6 +496,10 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x24FD, 0x0810, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x9110, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x8130, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0910, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0930, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0950, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0850, iwl8265_2ac_cfg)}, /* 9000 Series */ {IWL_PCI_DEVICE(0x2526, 0x0000, iwl9260_2ac_cfg)}, From 34777b00005525850c9f624186520745c5dd40b5 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Mon, 30 May 2016 12:52:43 +0300 Subject: [PATCH 15/56] iwlwifi: mvm: remove unnecessary device conversion when reading the MCC We convert the mvm device to a PCI device and then back again when trying to find the handle for the device's ACPI data. This is unnecessary, so it can be removed. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 25a98401a64f..182ec20200f8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -66,7 +66,6 @@ *****************************************************************************/ #include #include -#include #include #include "iwl-trans.h" #include "iwl-csr.h" @@ -802,9 +801,8 @@ static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc) struct acpi_buffer wrdd = {ACPI_ALLOCATE_BUFFER, NULL}; acpi_status status; u32 mcc_val; - struct pci_dev *pdev = to_pci_dev(mvm->dev); - root_handle = ACPI_HANDLE(&pdev->dev); + root_handle = ACPI_HANDLE(mvm->dev); if (!root_handle) { IWL_DEBUG_LAR(mvm, "Could not retrieve root port ACPI handle\n"); From d7fdd0e528c5184fd22153a317683549f0c58a19 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 19 May 2016 17:53:42 +0300 Subject: [PATCH 16/56] iwlwifi: pcie: poll RFH for RX DMA stop Somehow we ended up stopping RX using legacy RX registers even for devices that support RFH. Fix it. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-fh.h | 26 ++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 13 +++++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h index f08cdeef2d30..73f0ed88b20b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h @@ -344,6 +344,32 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl) #define RFH_RBDBUF_RBD0_LSB 0xA08300 #define RFH_RBDBUF_RBD_LSB(q) (RFH_RBDBUF_RBD0_LSB + (q) * 8) +/** + * RFH Status Register + * + * Bit fields: + * + * Bit 29: RBD_FETCH_IDLE + * This status flag is set by the RFH when there is no active RBD fetch from + * DRAM. + * Once the RFH RBD controller starts fetching (or when there is a pending + * RBD read response from DRAM), this flag is immediately turned off. + * + * Bit 30: SRAM_DMA_IDLE + * This status flag is set by the RFH when there is no active transaction from + * SRAM to DRAM. + * Once the SRAM to DRAM DMA is active, this flag is immediately turned off. + * + * Bit 31: RXF_DMA_IDLE + * This status flag is set by the RFH when there is no active transaction from + * RXF to DRAM. + * Once the RXF-to-DRAM DMA is active, this flag is immediately turned off. + */ +#define RFH_GEN_STATUS 0xA09808 +#define RBD_FETCH_IDLE BIT(29) +#define SRAM_DMA_IDLE BIT(30) +#define RXF_DMA_IDLE BIT(31) + /* DMA configuration */ #define RFH_RXF_DMA_CFG 0xA09820 /* RB size */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 8d4a60dcb32a..c1c3c6a86e59 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -173,9 +173,16 @@ static void iwl_pcie_write_prph_64_no_grab(struct iwl_trans *trans, u64 ofs, */ int iwl_pcie_rx_stop(struct iwl_trans *trans) { - iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); - return iwl_poll_direct_bit(trans, FH_MEM_RSSR_RX_STATUS_REG, - FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); + if (trans->cfg->mq_rx_supported) { + iwl_write_prph(trans, RFH_RXF_DMA_CFG, 0); + return iwl_poll_prph_bit(trans, RFH_GEN_STATUS, + RXF_DMA_IDLE, RXF_DMA_IDLE, 1000); + } else { + iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); + return iwl_poll_direct_bit(trans, FH_MEM_RSSR_RX_STATUS_REG, + FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, + 1000); + } } /* From ff7a68d0e7a7da411d0f87f57dde7e276e01ac92 Mon Sep 17 00:00:00 2001 From: Matti Gottlieb Date: Tue, 17 May 2016 10:50:08 +0300 Subject: [PATCH 17/56] iwlwifi: mvm: Do not open aggregations for null data packets Currently we try to open an aggregation for every packet (given that one is not already open). This causes redundant overhead (addba/delba) for null data packets. Do not open an aggregation for null data packets. Signed-off-by: Matti Gottlieb Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 13 +++++++------ drivers/net/wireless/intel/iwlwifi/mvm/rs.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 81dd2f6a48a5..7aecf46e2ab8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -399,7 +399,7 @@ static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags) static void rs_rate_scale_perform(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, - int tid); + int tid, bool ndp); static void rs_fill_lq_cmd(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, @@ -1161,7 +1161,7 @@ static u8 rs_get_tid(struct ieee80211_hdr *hdr) } void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - int tid, struct ieee80211_tx_info *info) + int tid, struct ieee80211_tx_info *info, bool ndp) { int legacy_success; int retries; @@ -1384,7 +1384,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, done: /* See if there's a better rate or modulation mode to try. */ if (sta->supp_rates[info->band]) - rs_rate_scale_perform(mvm, sta, lq_sta, tid); + rs_rate_scale_perform(mvm, sta, lq_sta, tid, ndp); } /* @@ -1407,7 +1407,8 @@ static void rs_mac80211_tx_status(void *mvm_r, info->flags & IEEE80211_TX_CTL_NO_ACK) return; - iwl_mvm_rs_tx_status(mvm, sta, rs_get_tid(hdr), info); + iwl_mvm_rs_tx_status(mvm, sta, rs_get_tid(hdr), info, + ieee80211_is_qos_nullfunc(hdr->frame_control)); } /* @@ -2213,7 +2214,7 @@ static bool rs_tpc_perform(struct iwl_mvm *mvm, static void rs_rate_scale_perform(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, - int tid) + int tid, bool ndp) { int low = IWL_RATE_INVALID; int high = IWL_RATE_INVALID; @@ -2512,7 +2513,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, (lq_sta->tx_agg_tid_en & (1 << tid)) && (tid != IWL_MAX_TID_COUNT)) { tid_data = &sta_priv->tid_data[tid]; - if (tid_data->state == IWL_AGG_OFF) { + if (tid_data->state == IWL_AGG_OFF && !ndp) { IWL_DEBUG_RATE(mvm, "try to aggregate tid %d\n", tid); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h index 90d046fb24a0..d4a7fe2e9aca 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h @@ -362,7 +362,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, /* Notify RS about Tx status */ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - int tid, struct ieee80211_tx_info *info); + int tid, struct ieee80211_tx_info *info, bool ndp); /** * iwl_rate_control_register - Register the rate control algorithm callbacks diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 9943013dec98..d91edabc9b8b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1660,7 +1660,7 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) iwl_mvm_tx_info_from_ba_notif(&ba_info, ba_notif, tid_data); IWL_DEBUG_TX_REPLY(mvm, "No reclaim. Update rs directly\n"); - iwl_mvm_rs_tx_status(mvm, sta, tid, &ba_info); + iwl_mvm_rs_tx_status(mvm, sta, tid, &ba_info, false); } out: From 99980ecffffe6af53361456be41c27e2fffc89cd Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Wed, 1 Jun 2016 11:46:20 +0300 Subject: [PATCH 18/56] iwlwifi: mvm: fix RX mpdu status enum FW sets status for each RX packet. Enum in the driver doesn't match with FW definition - fix it. Signed-off-by: Ayala Beker Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h index 1994331593cf..acc5cd53e4ba 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h @@ -296,7 +296,7 @@ enum iwl_rx_mpdu_status { IWL_RX_MPDU_STATUS_OVERRUN_OK = BIT(1), IWL_RX_MPDU_STATUS_SRC_STA_FOUND = BIT(2), IWL_RX_MPDU_STATUS_KEY_VALID = BIT(3), - IWL_RX_MPDU_STATUS_KEY_ERROR = BIT(4), + IWL_RX_MPDU_STATUS_KEY_PARAM_OK = BIT(4), IWL_RX_MPDU_STATUS_ICV_OK = BIT(5), IWL_RX_MPDU_STATUS_MIC_OK = BIT(6), IWL_RX_MPDU_RES_STATUS_TTAK_OK = BIT(7), @@ -311,7 +311,7 @@ enum iwl_rx_mpdu_status { IWL_RX_MPDU_STATUS_WEP_MATCH = BIT(12), IWL_RX_MPDU_STATUS_EXT_IV_MATCH = BIT(13), IWL_RX_MPDU_STATUS_KEY_ID_MATCH = BIT(14), - IWL_RX_MPDU_STATUS_KEY_COLOR = BIT(15), + IWL_RX_MPDU_STATUS_ROBUST_MNG_FRAME = BIT(15), }; enum iwl_rx_mpdu_hash_filter { From 855f492f655711259becb7d34978063ffc40fe12 Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Wed, 25 May 2016 18:31:39 +0300 Subject: [PATCH 19/56] iwlwifi: mvm: rs: add rate scaling support for 160MHz channels Expand TLC to support 160MHz channels. Full support for A-MSDU case will be added separately. Signed-off-by: Gregory Greenman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 81 +++++++++++++++------ drivers/net/wireless/intel/iwlwifi/mvm/rs.h | 1 + 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 7aecf46e2ab8..227c5ed9cbe6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -211,6 +211,9 @@ static bool rs_sgi_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, if (is_ht80(rate) && (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80)) return true; + if (is_ht160(rate) && (vht_cap->cap & + IEEE80211_VHT_CAP_SHORT_GI_160)) + return true; return false; } @@ -445,6 +448,13 @@ static const u16 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 241, 0, 475, 701, 921, 1343, 1741, 1931, 2117, 2468, 2691}, }; +static const u16 expected_tpt_siso_160MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 191, 0, 244, 288, 298, 308, 313, 318, 323, 328, 330}, + {0, 0, 0, 0, 200, 0, 251, 293, 302, 312, 317, 322, 327, 332, 334}, + {0, 0, 0, 0, 439, 0, 875, 1307, 1736, 2584, 3419, 3831, 4240, 5049, 5581}, + {0, 0, 0, 0, 488, 0, 972, 1451, 1925, 2864, 3785, 4240, 4691, 5581, 6165}, +}; + static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250, 261, 0}, {0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256, 267, 0}, @@ -466,6 +476,13 @@ static const u16 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 474, 0, 920, 1338, 1732, 2464, 3116, 3418, 3705, 4225, 4545}, }; +static const u16 expected_tpt_mimo2_160MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 240, 0, 278, 308, 313, 319, 322, 324, 328, 330, 334}, + {0, 0, 0, 0, 247, 0, 282, 310, 315, 320, 323, 325, 329, 332, 338}, + {0, 0, 0, 0, 875, 0, 1735, 2582, 3414, 5043, 6619, 7389, 8147, 9629, 10592}, + {0, 0, 0, 0, 971, 0, 1925, 2861, 3779, 5574, 7304, 8147, 8976, 10592, 11640}, +}; + /* mbps, mcs */ static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = { { "1", "BPSK DSSS"}, @@ -901,7 +918,6 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate, } } - WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_160); WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_80 && !is_vht(rate)); @@ -1495,6 +1511,9 @@ static const u16 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta, case RATE_MCS_CHAN_WIDTH_80: ht_tbl_pointer = expected_tpt_siso_80MHz; break; + case RATE_MCS_CHAN_WIDTH_160: + ht_tbl_pointer = expected_tpt_siso_160MHz; + break; default: WARN_ON_ONCE(1); } @@ -1509,6 +1528,9 @@ static const u16 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta, case RATE_MCS_CHAN_WIDTH_80: ht_tbl_pointer = expected_tpt_mimo2_80MHz; break; + case RATE_MCS_CHAN_WIDTH_160: + ht_tbl_pointer = expected_tpt_mimo2_160MHz; + break; default: WARN_ON_ONCE(1); } @@ -1583,12 +1605,17 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm, static u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta) { - if (sta->bandwidth >= IEEE80211_STA_RX_BW_80) + switch (sta->bandwidth) { + case IEEE80211_STA_RX_BW_160: + return RATE_MCS_CHAN_WIDTH_160; + case IEEE80211_STA_RX_BW_80: return RATE_MCS_CHAN_WIDTH_80; - else if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) + case IEEE80211_STA_RX_BW_40: return RATE_MCS_CHAN_WIDTH_40; - - return RATE_MCS_CHAN_WIDTH_20; + case IEEE80211_STA_RX_BW_20: + default: + return RATE_MCS_CHAN_WIDTH_20; + } } /* @@ -2566,6 +2593,9 @@ static const struct rs_init_rate_info rs_optimal_rates_ht[] = { { S8_MIN, IWL_RATE_MCS_0_INDEX}, }; +/* MCS index 9 is not valid for 20MHz VHT channel width, + * but is ok for 40, 80 and 160MHz channels. + */ static const struct rs_init_rate_info rs_optimal_rates_vht_20mhz[] = { { -60, IWL_RATE_MCS_8_INDEX }, { -64, IWL_RATE_MCS_7_INDEX }, @@ -2578,7 +2608,7 @@ static const struct rs_init_rate_info rs_optimal_rates_vht_20mhz[] = { { S8_MIN, IWL_RATE_MCS_0_INDEX}, }; -static const struct rs_init_rate_info rs_optimal_rates_vht_40_80mhz[] = { +static const struct rs_init_rate_info rs_optimal_rates_vht[] = { { -60, IWL_RATE_MCS_9_INDEX }, { -64, IWL_RATE_MCS_8_INDEX }, { -68, IWL_RATE_MCS_7_INDEX }, @@ -2641,9 +2671,9 @@ static void rs_init_optimal_rate(struct iwl_mvm *mvm, lq_sta->optimal_nentries = ARRAY_SIZE(rs_optimal_rates_vht_20mhz); } else { - lq_sta->optimal_rates = rs_optimal_rates_vht_40_80mhz; + lq_sta->optimal_rates = rs_optimal_rates_vht; lq_sta->optimal_nentries = - ARRAY_SIZE(rs_optimal_rates_vht_40_80mhz); + ARRAY_SIZE(rs_optimal_rates_vht); } } else if (is_ht(rate)) { lq_sta->optimal_rates = rs_optimal_rates_ht; @@ -2735,23 +2765,25 @@ static void rs_get_initial_rate(struct iwl_mvm *mvm, */ if (sta->vht_cap.vht_supported && best_rssi > IWL_RS_LOW_RSSI_THRESHOLD) { - if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) { - initial_rates = rs_optimal_rates_vht_40_80mhz; - nentries = ARRAY_SIZE(rs_optimal_rates_vht_40_80mhz); - if (sta->bandwidth >= IEEE80211_STA_RX_BW_80) - rate->bw = RATE_MCS_CHAN_WIDTH_80; - else - rate->bw = RATE_MCS_CHAN_WIDTH_40; - } else if (sta->bandwidth == IEEE80211_STA_RX_BW_20) { + switch (sta->bandwidth) { + case IEEE80211_STA_RX_BW_160: + case IEEE80211_STA_RX_BW_80: + case IEEE80211_STA_RX_BW_40: + initial_rates = rs_optimal_rates_vht; + nentries = ARRAY_SIZE(rs_optimal_rates_vht); + break; + case IEEE80211_STA_RX_BW_20: initial_rates = rs_optimal_rates_vht_20mhz; nentries = ARRAY_SIZE(rs_optimal_rates_vht_20mhz); - rate->bw = RATE_MCS_CHAN_WIDTH_20; - } else { + break; + default: IWL_ERR(mvm, "Invalid BW %d\n", sta->bandwidth); goto out; } + active_rate = lq_sta->active_siso_rate; rate->type = LQ_VHT_SISO; + rate->bw = rs_bw_from_sta_bw(sta); } else if (sta->ht_cap.ht_supported && best_rssi > IWL_RS_LOW_RSSI_THRESHOLD) { initial_rates = rs_optimal_rates_ht; @@ -3058,6 +3090,9 @@ void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg) case RATE_MCS_CHAN_WIDTH_80: mvm->drv_rx_stats.bw_80_frames++; break; + case RATE_MCS_CHAN_WIDTH_160: + mvm->drv_rx_stats.bw_160_frames++; + break; default: WARN_ONCE(1, "bad BW. rate 0x%x", rate); } @@ -3706,7 +3741,8 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, desc += sprintf(buff + desc, " %s", (is_ht20(rate)) ? "20MHz" : (is_ht40(rate)) ? "40MHz" : - (is_ht80(rate)) ? "80Mhz" : "BAD BW"); + (is_ht80(rate)) ? "80MHz" : + (is_ht160(rate)) ? "160MHz" : "BAD BW"); desc += sprintf(buff + desc, " %s %s %s %s\n", (rate->sgi) ? "SGI" : "NGI", (rate->ldpc) ? "LDPC" : "BCC", @@ -3788,9 +3824,10 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, lq_sta->active_tbl == i ? "*" : "x", rate->type, rate->sgi, - is_ht20(rate) ? "20Mhz" : - is_ht40(rate) ? "40Mhz" : - is_ht80(rate) ? "80Mhz" : "ERR", + is_ht20(rate) ? "20MHz" : + is_ht40(rate) ? "40MHz" : + is_ht80(rate) ? "80MHz" : + is_ht160(rate) ? "160MHz" : "ERR", rate->index); for (j = 0; j < IWL_RATE_COUNT; j++) { desc += sprintf(buff+desc, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h index d4a7fe2e9aca..ee207f2c0a90 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h @@ -205,6 +205,7 @@ struct rs_rate { #define is_ht20(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_20) #define is_ht40(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_40) #define is_ht80(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_80) +#define is_ht160(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_160) #define IWL_MAX_MCS_DISPLAY_SIZE 12 From 5a7d87da8d9b9f04ecdbebe7e5710a1391f85fa8 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 27 May 2016 15:07:03 +0200 Subject: [PATCH 20/56] iwlwifi: mvm: avoid harmless -Wmaybe-uninialized warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gcc is apparently unablel to track the state of the local 'resp_v2' variable across the kzalloc() function, and warns about the response variable being used without an initialization: drivers/net/wireless/intel/iwlwifi/mvm/nvm.c: In function ‘iwl_mvm_update_mcc’: drivers/net/wireless/intel/iwlwifi/mvm/nvm.c:727:36: warning: ‘mcc_resp_v1’ may be used uninitialized in this function [-Wmaybe-uninitialized] resp_cp->n_channels = mcc_resp_v1->n_channels; drivers/net/wireless/intel/iwlwifi/mvm/nvm.c:721:3: warning: ‘mcc_resp’ may be used uninitialized in this function [-Wmaybe-uninitialized] memcpy(resp_cp, mcc_resp, resp_len); The warning showed up in x86 allmodconfig after my patch to unhide -Wmaybe-uninitialized warnings by default was merged, though it always existed in randconfig builds. I did not catch the warning earlier because I was testing on ARM, which never produced the warning. This rearranges the code in a way that improves readability for both humans and the compiler, and that avoids the warning. Signed-off-by: Arnd Bergmann Fixes: 6fa52430f0b3 ("iwlwifi: mvm: change mcc update API") Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 41 ++++++++++---------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 182ec20200f8..7a686f67f007 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -666,8 +666,7 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, .mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]), .source_id = (u8)src_id, }; - struct iwl_mcc_update_resp *mcc_resp, *resp_cp = NULL; - struct iwl_mcc_update_resp_v1 *mcc_resp_v1 = NULL; + struct iwl_mcc_update_resp *resp_cp; struct iwl_rx_packet *pkt; struct iwl_host_cmd cmd = { .id = MCC_UPDATE_CMD, @@ -700,34 +699,36 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, /* Extract MCC response */ if (resp_v2) { - mcc_resp = (void *)pkt->data; + struct iwl_mcc_update_resp *mcc_resp = (void *)pkt->data; + n_channels = __le32_to_cpu(mcc_resp->n_channels); + resp_len = sizeof(struct iwl_mcc_update_resp) + + n_channels * sizeof(__le32); + resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL); } else { - mcc_resp_v1 = (void *)pkt->data; + struct iwl_mcc_update_resp_v1 *mcc_resp_v1 = (void *)pkt->data; + n_channels = __le32_to_cpu(mcc_resp_v1->n_channels); + resp_len = sizeof(struct iwl_mcc_update_resp) + + n_channels * sizeof(__le32); + resp_cp = kzalloc(resp_len, GFP_KERNEL); + + if (resp_cp) { + resp_cp->status = mcc_resp_v1->status; + resp_cp->mcc = mcc_resp_v1->mcc; + resp_cp->cap = mcc_resp_v1->cap; + resp_cp->source_id = mcc_resp_v1->source_id; + resp_cp->n_channels = mcc_resp_v1->n_channels; + memcpy(resp_cp->channels, mcc_resp_v1->channels, + n_channels * sizeof(__le32)); + } } - resp_len = sizeof(struct iwl_mcc_update_resp) + n_channels * - sizeof(__le32); - - resp_cp = kzalloc(resp_len, GFP_KERNEL); if (!resp_cp) { ret = -ENOMEM; goto exit; } - if (resp_v2) { - memcpy(resp_cp, mcc_resp, resp_len); - } else { - resp_cp->status = mcc_resp_v1->status; - resp_cp->mcc = mcc_resp_v1->mcc; - resp_cp->cap = mcc_resp_v1->cap; - resp_cp->source_id = mcc_resp_v1->source_id; - resp_cp->n_channels = mcc_resp_v1->n_channels; - memcpy(resp_cp->channels, mcc_resp_v1->channels, - n_channels * sizeof(__le32)); - } - status = le32_to_cpu(resp_cp->status); mcc = le16_to_cpu(resp_cp->mcc); From 2c4a247e42526d9aae8f5ce1f190b893532f2806 Mon Sep 17 00:00:00 2001 From: Oren Givon Date: Sun, 29 May 2016 14:05:50 +0300 Subject: [PATCH 21/56] iwlwifi: mvm: fix txq aggregation bug Fix an issue where nullfunc frames and block ack requests had the same tid as aggregation frames and were queued on a non aggregation queue. The pending frames counter included those frames but the check whether to decrement the pending frames counter relied on the tid status and not on the txq id. The result was an inconsistent state of the pending frames counter followed by a failure to remove the station. This failure triggered SYSASSERT 0x3421. In addition, fix a situation in DQA mode where the number of pending frames turned negative. This was due to the TX queue being on the IWL_EMPTYING_HW_QUEUE_DELBA state and its frames were still decremented. Even though the SYSASSERT issue is fixed when DQA is disabled, the issue is not completely solved when DQA is enabled and should still be fixed. Signed-off-by: Oren Givon Fixes: cf961e16620f ("iwlwifi: mvm: support dqa-mode agg on non-shared queue") Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index d91edabc9b8b..1f23ceeb2350 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1329,7 +1329,15 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, bool send_eosp_ndp = false; spin_lock_bh(&mvmsta->lock); - txq_agg = (mvmsta->tid_data[tid].state == IWL_AGG_ON); + if (iwl_mvm_is_dqa_supported(mvm)) { + enum iwl_mvm_agg_state state; + + state = mvmsta->tid_data[tid].state; + txq_agg = (state == IWL_AGG_ON || + state == IWL_EMPTYING_HW_QUEUE_DELBA); + } else { + txq_agg = txq_id >= mvm->first_agg_queue; + } if (!is_ndp) { tid_data->next_reclaimed = next_reclaimed; From e160f635abd409748908912d1c528721127b2fc1 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 3 Jun 2016 14:39:19 -0700 Subject: [PATCH 22/56] iwlwifi: dvm: Remove unused array 'iwlagn_loose_lookup' gcc-6 reports the following error if -Werror=unused-const-variable is enabled. drivers/net/wireless/intel/iwlwifi/dvm/lib.c:210:21: error: 'iwlagn_loose_lookup' defined but not used Signed-off-by: Guenter Roeck Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/dvm/lib.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c index 8dda52ae3bb5..6c2d6da7eec6 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c @@ -205,23 +205,6 @@ static const __le32 iwlagn_def_3w_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { cpu_to_le32(0xf0005000), }; - -/* Loose Coex */ -static const __le32 iwlagn_loose_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaeaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xcc00ff28), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xcc00aaaa), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), -}; - /* Full concurrency */ static const __le32 iwlagn_concurrent_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { cpu_to_le32(0xaaaaaaaa), From f65ebd888c5660e97b73fdcb94156a4f357a885d Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Sun, 22 May 2016 16:25:40 +0300 Subject: [PATCH 23/56] iwlwifi: add dump of RFH Add support of dumping new RFH instead of FH registers. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-io.c | 115 +++++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index 32c8f84ae519..d8b430688872 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -1,7 +1,7 @@ /****************************************************************************** * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2015 - 2016 Intel Deutschland GmbH * * Portions of this file are derived from the ipw3945 project. * @@ -228,9 +228,117 @@ void iwl_force_nmi(struct iwl_trans *trans) } IWL_EXPORT_SYMBOL(iwl_force_nmi); -static const char *get_fh_string(int cmd) +static const char *get_rfh_string(int cmd) { #define IWL_CMD(x) case x: return #x +#define IWL_CMD_MQ(arg, reg, q) { if (arg == reg(q)) return #reg; } + + int i; + + for (i = 0; i < IWL_MAX_RX_HW_QUEUES; i++) { + IWL_CMD_MQ(cmd, RFH_Q_FRBDCB_BA_LSB, i); + IWL_CMD_MQ(cmd, RFH_Q_FRBDCB_WIDX, i); + IWL_CMD_MQ(cmd, RFH_Q_FRBDCB_RIDX, i); + IWL_CMD_MQ(cmd, RFH_Q_URBD_STTS_WPTR_LSB, i); + }; + + switch (cmd) { + IWL_CMD(RFH_RXF_DMA_CFG); + IWL_CMD(RFH_GEN_CFG); + IWL_CMD(RFH_GEN_STATUS); + IWL_CMD(FH_TSSR_TX_STATUS_REG); + IWL_CMD(FH_TSSR_TX_ERROR_REG); + default: + return "UNKNOWN"; + } +#undef IWL_CMD_MQ +} + +struct reg { + u32 addr; + bool is64; +}; + +static int iwl_dump_rfh(struct iwl_trans *trans, char **buf) +{ + int i, q; + int num_q = trans->num_rx_queues; + static const u32 rfh_tbl[] = { + RFH_RXF_DMA_CFG, + RFH_GEN_CFG, + RFH_GEN_STATUS, + FH_TSSR_TX_STATUS_REG, + FH_TSSR_TX_ERROR_REG, + }; + static const struct reg rfh_mq_tbl[] = { + { RFH_Q0_FRBDCB_BA_LSB, true }, + { RFH_Q0_FRBDCB_WIDX, false }, + { RFH_Q0_FRBDCB_RIDX, false }, + { RFH_Q0_URBD_STTS_WPTR_LSB, true }, + }; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (buf) { + int pos = 0; + /* + * Register (up to 34 for name + 8 blank/q for MQ): 40 chars + * Colon + space: 2 characters + * 0X%08x: 10 characters + * New line: 1 character + * Total of 53 characters + */ + size_t bufsz = ARRAY_SIZE(rfh_tbl) * 53 + + ARRAY_SIZE(rfh_mq_tbl) * 53 * num_q + 40; + + *buf = kmalloc(bufsz, GFP_KERNEL); + if (!*buf) + return -ENOMEM; + + pos += scnprintf(*buf + pos, bufsz - pos, + "RFH register values:\n"); + + for (i = 0; i < ARRAY_SIZE(rfh_tbl); i++) + pos += scnprintf(*buf + pos, bufsz - pos, + "%40s: 0X%08x\n", + get_rfh_string(rfh_tbl[i]), + iwl_read_prph(trans, rfh_tbl[i])); + + for (i = 0; i < ARRAY_SIZE(rfh_mq_tbl); i++) + for (q = 0; q < num_q; q++) { + u32 addr = rfh_mq_tbl[i].addr; + + addr += q * (rfh_mq_tbl[i].is64 ? 8 : 4); + pos += scnprintf(*buf + pos, bufsz - pos, + "%34s(q %2d): 0X%08x\n", + get_rfh_string(addr), q, + iwl_read_prph(trans, addr)); + } + + return pos; + } +#endif + + IWL_ERR(trans, "RFH register values:\n"); + for (i = 0; i < ARRAY_SIZE(rfh_tbl); i++) + IWL_ERR(trans, " %34s: 0X%08x\n", + get_rfh_string(rfh_tbl[i]), + iwl_read_prph(trans, rfh_tbl[i])); + + for (i = 0; i < ARRAY_SIZE(rfh_mq_tbl); i++) + for (q = 0; q < num_q; q++) { + u32 addr = rfh_mq_tbl[i].addr; + + addr += q * (rfh_mq_tbl[i].is64 ? 8 : 4); + IWL_ERR(trans, " %34s(q %d): 0X%08x\n", + get_rfh_string(addr), q, + iwl_read_prph(trans, addr)); + } + + return 0; +} + +static const char *get_fh_string(int cmd) +{ switch (cmd) { IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG); IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG); @@ -262,6 +370,9 @@ int iwl_dump_fh(struct iwl_trans *trans, char **buf) FH_TSSR_TX_ERROR_REG }; + if (trans->cfg->mq_rx_supported) + return iwl_dump_rfh(trans, buf); + #ifdef CONFIG_IWLWIFI_DEBUGFS if (buf) { int pos = 0; From 9c07e9aafcbe4b2a98c18d9b0ee28c84d749ea84 Mon Sep 17 00:00:00 2001 From: Golan Ben-Ami Date: Thu, 19 May 2016 07:46:20 +0300 Subject: [PATCH 24/56] iwlwifi: Reserve iwl_fw_error_dump_type enum Reserve a single iwl_fw_error_dump_type enum for external code utilities. Signed-off-by: Golan Ben-Ami Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h index 09b7ea28f4a0..420c31dab263 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h @@ -89,6 +89,9 @@ * @IWL_FW_ERROR_PAGING: UMAC's image memory segments which were * paged to the DRAM. * @IWL_FW_ERROR_DUMP_RADIO_REG: Dump the radio registers. + * @IWL_FW_ERROR_DUMP_EXTERNAL: used only by external code utilities, and + * for that reason is not in use in any other place in the Linux Wi-Fi + * stack. */ enum iwl_fw_error_dump_type { /* 0 is deprecated */ @@ -106,6 +109,7 @@ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_PAGING = 12, IWL_FW_ERROR_DUMP_RADIO_REG = 13, IWL_FW_ERROR_DUMP_INTERNAL_TXF = 14, + IWL_FW_ERROR_DUMP_EXTERNAL = 15, /* Do not move */ IWL_FW_ERROR_DUMP_MAX, }; From 2a53d166c464d7b51d6f7204d445fc0254b25f51 Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Thu, 7 Apr 2016 16:21:57 +0300 Subject: [PATCH 25/56] iwlwifi: mvm: add support for GCMP encryption Newer hardware supports GCMP and GCMP 256-bit ciphers. Add support for adding/setting GCMP key for TX mode. In the TX command handling GCMP-256 is handled in a different way as the key size should be up to 128-bits: Set the key value to the key index in the key table, and specify that this key should be taken form the key table instead of from the TX command. While at it - convert security control flags to an enum. Signed-off-by: Ayala Beker Signed-off-by: Luca Coelho --- .../wireless/intel/iwlwifi/mvm/fw-api-sta.h | 8 ++-- .../wireless/intel/iwlwifi/mvm/fw-api-tx.h | 31 +++++++++++---- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 19 +++++++-- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 +- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 9 +++++ drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 39 ++++++++++++++----- 6 files changed, 85 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h index 38b1d045be8e..d1c4fb849111 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h @@ -141,6 +141,7 @@ enum iwl_sta_flags { * @STA_KEY_FLG_CCM: CCMP encryption algorithm * @STA_KEY_FLG_TKIP: TKIP encryption algorithm * @STA_KEY_FLG_EXT: extended cipher algorithm (depends on the FW support) + * @STA_KEY_FLG_GCMP: GCMP encryption algorithm * @STA_KEY_FLG_CMAC: CMAC encryption algorithm * @STA_KEY_FLG_ENC_UNKNOWN: unknown encryption algorithm * @STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value @@ -149,6 +150,7 @@ enum iwl_sta_flags { * @STA_KEY_FLG_KEYID_MSK: the index of the key * @STA_KEY_NOT_VALID: key is invalid * @STA_KEY_FLG_WEP_13BYTES: set for 13 bytes WEP key + * @STA_KEY_FLG_KEY_32BYTES for non-wep key set for 32 bytes key * @STA_KEY_MULTICAST: set for multical key * @STA_KEY_MFP: key is used for Management Frame Protection */ @@ -158,6 +160,7 @@ enum iwl_sta_key_flag { STA_KEY_FLG_CCM = (2 << 0), STA_KEY_FLG_TKIP = (3 << 0), STA_KEY_FLG_EXT = (4 << 0), + STA_KEY_FLG_GCMP = (5 << 0), STA_KEY_FLG_CMAC = (6 << 0), STA_KEY_FLG_ENC_UNKNOWN = (7 << 0), STA_KEY_FLG_EN_MSK = (7 << 0), @@ -167,6 +170,7 @@ enum iwl_sta_key_flag { STA_KEY_FLG_KEYID_MSK = (3 << STA_KEY_FLG_KEYID_POS), STA_KEY_NOT_VALID = BIT(11), STA_KEY_FLG_WEP_13BYTES = BIT(12), + STA_KEY_FLG_KEY_32BYTES = BIT(12), STA_KEY_MULTICAST = BIT(14), STA_KEY_MFP = BIT(15), }; @@ -388,7 +392,6 @@ struct iwl_mvm_add_sta_cmd { * @key_offset: key offset in key storage * @key_flags: type %iwl_sta_key_flag * @key: key material data - * @key2: key material data * @rx_secur_seq_cnt: RX security sequence counter for the key * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx @@ -397,8 +400,7 @@ struct iwl_mvm_add_sta_key_cmd { u8 sta_id; u8 key_offset; __le16 key_flags; - u8 key[16]; - u8 key2[16]; + u8 key[32]; u8 rx_secur_seq_cnt[16]; u8 tkip_rx_tsc_byte2; u8 reserved; 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 ee59511323c1..4144623e1616 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h @@ -137,17 +137,32 @@ enum iwl_tx_pm_timeouts { PM_FRAME_ASSOC = 3, }; -/* - * TX command security control - */ -#define TX_CMD_SEC_WEP 0x01 -#define TX_CMD_SEC_CCM 0x02 -#define TX_CMD_SEC_TKIP 0x03 -#define TX_CMD_SEC_EXT 0x04 #define TX_CMD_SEC_MSK 0x07 #define TX_CMD_SEC_WEP_KEY_IDX_POS 6 #define TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0 -#define TX_CMD_SEC_KEY128 0x08 + +/** + * enum iwl_tx_cmd_sec_ctrl - bitmasks for security control in TX command + * @TX_CMD_SEC_WEP: WEP encryption algorithm. + * @TX_CMD_SEC_CCM: CCM encryption algorithm. + * @TX_CMD_SEC_TKIP: TKIP encryption algorithm. + * @TX_CMD_SEC_EXT: extended cipher algorithm. + * @TX_CMD_SEC_GCMP: GCMP encryption algorithm. + * @TX_CMD_SEC_KEY128: set for 104 bits WEP key. + * @TC_CMD_SEC_KEY_FROM_TABLE: for a non-WEP key, set if the key should be taken + * from the table instead of from the TX command. + * If the key is taken from the key table its index should be given by the + * first byte of the TX command key field. + */ +enum iwl_tx_cmd_sec_ctrl { + TX_CMD_SEC_WEP = 0x01, + TX_CMD_SEC_CCM = 0x02, + TX_CMD_SEC_TKIP = 0x03, + TX_CMD_SEC_EXT = 0x04, + TX_CMD_SEC_GCMP = 0x05, + TX_CMD_SEC_KEY128 = 0x08, + TC_CMD_SEC_KEY_FROM_TABLE = 0x08, +}; /* TODO: how does these values are OK with only 16 bit variable??? */ /* diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index af1d266d6fe8..6b158f0de39d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -465,11 +465,20 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES; hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; - BUILD_BUG_ON(ARRAY_SIZE(mvm->ciphers) < ARRAY_SIZE(mvm_ciphers) + 2); + BUILD_BUG_ON(ARRAY_SIZE(mvm->ciphers) < ARRAY_SIZE(mvm_ciphers) + 4); memcpy(mvm->ciphers, mvm_ciphers, sizeof(mvm_ciphers)); hw->wiphy->n_cipher_suites = ARRAY_SIZE(mvm_ciphers); hw->wiphy->cipher_suites = mvm->ciphers; + if (iwl_mvm_has_new_rx_api(mvm)) { + mvm->ciphers[hw->wiphy->n_cipher_suites] = + WLAN_CIPHER_SUITE_GCMP; + hw->wiphy->n_cipher_suites++; + mvm->ciphers[hw->wiphy->n_cipher_suites] = + WLAN_CIPHER_SUITE_GCMP_256; + hw->wiphy->n_cipher_suites++; + } + /* * Enable 11w if advertised by firmware and software crypto * is not enabled (as the firmware will interpret some mgmt @@ -2721,6 +2730,8 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; break; case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; break; case WLAN_CIPHER_SUITE_AES_CMAC: @@ -2782,7 +2793,8 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, sta && iwl_mvm_has_new_rx_api(mvm) && key->flags & IEEE80211_KEY_FLAG_PAIRWISE && (key->cipher == WLAN_CIPHER_SUITE_CCMP || - key->cipher == WLAN_CIPHER_SUITE_GCMP)) { + key->cipher == WLAN_CIPHER_SUITE_GCMP || + key->cipher == WLAN_CIPHER_SUITE_GCMP_256)) { struct ieee80211_key_seq seq; int tid, q; @@ -2836,7 +2848,8 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, if (sta && iwl_mvm_has_new_rx_api(mvm) && key->flags & IEEE80211_KEY_FLAG_PAIRWISE && (key->cipher == WLAN_CIPHER_SUITE_CCMP || - key->cipher == WLAN_CIPHER_SUITE_GCMP)) { + key->cipher == WLAN_CIPHER_SUITE_GCMP || + key->cipher == WLAN_CIPHER_SUITE_GCMP_256)) { mvmsta = iwl_mvm_sta_from_mac80211(sta); ptk_pn = rcu_dereference_protected( mvmsta->ptk_pn[keyidx], diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 6092af23969f..e240f198e001 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -707,6 +707,7 @@ enum iwl_mvm_queue_status { }; #define IWL_MVM_DQA_QUEUE_TIMEOUT (5 * HZ) +#define IWL_MVM_NUM_CIPHERS 8 struct iwl_mvm { /* for logger access */ @@ -1015,7 +1016,7 @@ struct iwl_mvm { struct iwl_mvm_shared_mem_cfg shared_mem_cfg; - u32 ciphers[6]; + u32 ciphers[IWL_MVM_NUM_CIPHERS]; struct iwl_mvm_tof_data tof_data; struct ieee80211_vif *nan_vif; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index cf0681765ec4..7321bb6b8d67 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -2243,6 +2243,13 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, key_flags |= cpu_to_le16(STA_KEY_FLG_WEP); memcpy(cmd.key + 3, keyconf->key, keyconf->keylen); break; + case WLAN_CIPHER_SUITE_GCMP_256: + key_flags |= cpu_to_le16(STA_KEY_FLG_KEY_32BYTES); + /* fall through */ + case WLAN_CIPHER_SUITE_GCMP: + key_flags |= cpu_to_le16(STA_KEY_FLG_GCMP); + memcpy(cmd.key, keyconf->key, keyconf->keylen); + break; default: key_flags |= cpu_to_le16(STA_KEY_FLG_EXT); memcpy(cmd.key, keyconf->key, keyconf->keylen); @@ -2363,6 +2370,8 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm, case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, 0, NULL, 0, key_offset); break; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 1f23ceeb2350..2373142b0f43 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -388,6 +388,23 @@ void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd, tx_cmd->rate_n_flags = cpu_to_le32((u32)rate_plcp | rate_flags); } +static inline void iwl_mvm_set_tx_cmd_pn(struct ieee80211_tx_info *info, + u8 *crypto_hdr) +{ + struct ieee80211_key_conf *keyconf = info->control.hw_key; + u64 pn; + + pn = atomic64_inc_return(&keyconf->tx_pn); + crypto_hdr[0] = pn; + crypto_hdr[2] = 0; + crypto_hdr[3] = 0x20 | (keyconf->keyidx << 6); + crypto_hdr[1] = pn >> 8; + crypto_hdr[4] = pn >> 16; + crypto_hdr[5] = pn >> 24; + crypto_hdr[6] = pn >> 32; + crypto_hdr[7] = pn >> 40; +} + /* * Sets the fields in the Tx cmd that are crypto related */ @@ -405,15 +422,7 @@ static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm, case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP_256: iwl_mvm_set_tx_cmd_ccmp(info, tx_cmd); - pn = atomic64_inc_return(&keyconf->tx_pn); - crypto_hdr[0] = pn; - crypto_hdr[2] = 0; - crypto_hdr[3] = 0x20 | (keyconf->keyidx << 6); - crypto_hdr[1] = pn >> 8; - crypto_hdr[4] = pn >> 16; - crypto_hdr[5] = pn >> 24; - crypto_hdr[6] = pn >> 32; - crypto_hdr[7] = pn >> 40; + iwl_mvm_set_tx_cmd_pn(info, crypto_hdr); break; case WLAN_CIPHER_SUITE_TKIP: @@ -433,6 +442,18 @@ static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm, memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); break; + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + /* TODO: Taking the key from the table might introduce a race + * when PTK rekeying is done, having an old packets with a PN + * based on the old key but the message encrypted with a new + * one. + * Need to handle this. + */ + tx_cmd->sec_ctl |= TX_CMD_SEC_GCMP | TC_CMD_SEC_KEY_FROM_TABLE; + tx_cmd->key[0] = keyconf->hw_key_idx; + iwl_mvm_set_tx_cmd_pn(info, crypto_hdr); + break; default: tx_cmd->sec_ctl |= TX_CMD_SEC_EXT; } From 988b59684d7681bfb4eb508eeed3abb2bfde7af6 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Sun, 10 Apr 2016 15:06:55 +0300 Subject: [PATCH 26/56] iwlwifi: mvm: support new statistics notification For 9000 family we will get extended statistics notification with averaged data for RSSI, TCM and rogue AP detection. Support it. Future patches will added the required algorithms. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- .../wireless/intel/iwlwifi/mvm/fw-api-mac.h | 3 +++ .../wireless/intel/iwlwifi/mvm/fw-api-stats.h | 24 ++++++++++++++++- .../net/wireless/intel/iwlwifi/mvm/fw-api.h | 3 --- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 5 ++++ drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 26 +++++++++++++++++-- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 1 + 6 files changed, 56 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h index 95ac59d088b1..0246506ab595 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h @@ -72,6 +72,9 @@ #define NUM_MAC_INDEX_DRIVER MAC_INDEX_AUX #define NUM_MAC_INDEX (MAC_INDEX_AUX + 1) +#define IWL_MVM_STATION_COUNT 16 +#define IWL_MVM_TDLS_STA_COUNT 4 + enum iwl_ac { AC_BK, AC_BE, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h index 438665a54923..4e638a44babb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 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 @@ -252,6 +253,20 @@ struct mvm_statistics_general_v8 { u8 reserved[4 - (NUM_MAC_INDEX % 4)]; } __packed; /* STATISTICS_GENERAL_API_S_VER_8 */ +/** + * struct mvm_statistics_load - RX statistics for multi-queue devices + * @air_time: accumulated air time, per mac + * @byte_count: accumulated byte count, per mac + * @pkt_count: accumulated packet count, per mac + * @avg_energy: average RSSI, per station + */ +struct mvm_statistics_load { + __le32 air_time[NUM_MAC_INDEX]; + __le32 byte_count[NUM_MAC_INDEX]; + __le32 pkt_count[NUM_MAC_INDEX]; + u8 avg_energy[IWL_MVM_STATION_COUNT]; +} __packed; /* STATISTICS_RX_MAC_STATION_S_VER_1 */ + struct mvm_statistics_rx { struct mvm_statistics_rx_phy ofdm; struct mvm_statistics_rx_phy cck; @@ -266,7 +281,6 @@ struct mvm_statistics_rx { * while associated. To disable this behavior, set DISABLE_NOTIF flag in the * STATISTICS_CMD (0x9c), below. */ - struct iwl_notif_statistics_v10 { __le32 flag; struct mvm_statistics_rx rx; @@ -274,6 +288,14 @@ struct iwl_notif_statistics_v10 { struct mvm_statistics_general_v8 general; } __packed; /* STATISTICS_NTFY_API_S_VER_10 */ +struct iwl_notif_statistics_v11 { + __le32 flag; + struct mvm_statistics_rx rx; + struct mvm_statistics_tx tx; + struct mvm_statistics_general_v8 general; + struct mvm_statistics_load load_stats; +} __packed; /* STATISTICS_NTFY_API_S_VER_11 */ + #define IWL_STATISTICS_FLG_CLEAR 0x1 #define IWL_STATISTICS_FLG_DISABLE_NOTIF 0x2 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index eea03ecafbe0..71076f02796e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -129,9 +129,6 @@ enum iwl_mvm_tx_fifo { IWL_MVM_TX_FIFO_CMD = 7, }; -#define IWL_MVM_STATION_COUNT 16 - -#define IWL_MVM_TDLS_STA_COUNT 4 /* commands */ enum { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 6b158f0de39d..5c044eac1eed 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3922,6 +3922,11 @@ static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + if (mvmsta->avg_energy) { + sinfo->signal_avg = mvmsta->avg_energy; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG); + } + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS)) return; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 2d5a7cc771d2..0e60e38b2acf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -498,6 +498,7 @@ struct iwl_mvm_stat_data { __le32 mac_id; u8 beacon_filter_average_energy; struct mvm_statistics_general_v8 *general; + struct mvm_statistics_load *load; }; static void iwl_mvm_stat_iterator(void *_data, u8 *mac, @@ -614,13 +615,15 @@ iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { - struct iwl_notif_statistics_v10 *stats = (void *)&pkt->data; + struct iwl_notif_statistics_v11 *stats = (void *)&pkt->data; struct iwl_mvm_stat_data data = { .mvm = mvm, }; + int expected_size = iwl_mvm_has_new_rx_api(mvm) ? sizeof(*stats) : + sizeof(struct iwl_notif_statistics_v10); u32 temperature; - if (iwl_rx_packet_payload_len(pkt) != sizeof(*stats)) + if (iwl_rx_packet_payload_len(pkt) != expected_size) goto invalid; temperature = le32_to_cpu(stats->general.radio_temperature); @@ -638,6 +641,25 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, le64_to_cpu(stats->general.on_time_scan); data.general = &stats->general; + if (iwl_mvm_has_new_rx_api(mvm)) { + int i; + + data.load = &stats->load_stats; + + rcu_read_lock(); + for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + struct iwl_mvm_sta *sta; + + if (!data.load->avg_energy[i]) + continue; + + sta = iwl_mvm_sta_from_staid_rcu(mvm, i); + if (!sta) + continue; + sta->avg_energy = data.load->avg_energy[i]; + } + rcu_read_unlock(); + } iwl_mvm_rx_stats_check_trigger(mvm, pkt); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 1588eb63b7ec..bbc1cab2c3bf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -438,6 +438,7 @@ struct iwl_mvm_sta { bool tlc_amsdu; u8 agg_tids; u8 sleep_tx_count; + u8 avg_energy; }; static inline struct iwl_mvm_sta * From e34d975e40ff69d55cb45f968f5ca0892e114b92 Mon Sep 17 00:00:00 2001 From: Haim Dreyfuss Date: Tue, 31 May 2016 11:13:09 +0300 Subject: [PATCH 27/56] iwlwifi: Add a000 HW family support Add a000 family configuration to iwl-cfg struct Signed-off-by: Haim Dreyfuss Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/Makefile | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-a000.c | 130 ++++++++++++++++++ .../net/wireless/intel/iwlwifi/iwl-config.h | 2 + drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 3 + 4 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-a000.c diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile index 05828c61d1ab..6e7ed908de0c 100644 --- a/drivers/net/wireless/intel/iwlwifi/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/Makefile @@ -8,7 +8,7 @@ iwlwifi-objs += iwl-eeprom-read.o iwl-eeprom-parse.o iwlwifi-objs += iwl-phy-db.o iwl-nvm-parse.o iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o iwlwifi-$(CONFIG_IWLDVM) += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o -iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o iwl-8000.o iwl-9000.o +iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o iwl-8000.o iwl-9000.o iwl-a000.o iwlwifi-objs += iwl-trans.o iwlwifi-objs += $(iwlwifi-m) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c new file mode 100644 index 000000000000..220767e02734 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c @@ -0,0 +1,130 @@ +/****************************************************************************** + * + * 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) 2015-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 + * 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. + * + * BSD LICENSE + * + * Copyright(c) 2015-2016 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 +#include +#include "iwl-config.h" +#include "iwl-agn-hw.h" + +/* Highest firmware API version supported */ +#define IWL_A000_UCODE_API_MAX 24 + +/* Lowest firmware API version supported */ +#define IWL_A000_UCODE_API_MIN 24 + +/* NVM versions */ +#define IWL_A000_NVM_VERSION 0x0a1d +#define IWL_A000_TX_POWER_VERSION 0xffff /* meaningless */ + +/* Memory offsets and lengths */ +#define IWL_A000_DCCM_OFFSET 0x800000 +#define IWL_A000_DCCM_LEN 0x18000 +#define IWL_A000_DCCM2_OFFSET 0x880000 +#define IWL_A000_DCCM2_LEN 0x8000 +#define IWL_A000_SMEM_OFFSET 0x400000 +#define IWL_A000_SMEM_LEN 0x68000 + +#define IWL_A000_FW_PRE "iwlwifi-Qu-a0-jf-b0-" +#define IWL_A000_MODULE_FIRMWARE(api) \ + IWL_A000_FW_PRE "-" __stringify(api) ".ucode" + +#define NVM_HW_SECTION_NUM_FAMILY_A000 10 + +static const struct iwl_base_params iwl_a000_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_A000, + .num_of_queues = 31, + .shadow_ram_support = true, + .led_compensation = 57, + .wd_timeout = IWL_LONG_WD_TIMEOUT, + .max_event_log_size = 512, + .shadow_reg_enable = true, + .pcie_l1_allowed = true, +}; + +static const struct iwl_ht_params iwl_a000_ht_params = { + .stbc = true, + .ldpc = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), +}; + +#define IWL_DEVICE_A000 \ + .ucode_api_max = IWL_A000_UCODE_API_MAX, \ + .ucode_api_min = IWL_A000_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_8000, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .base_params = &iwl_a000_base_params, \ + .led_mode = IWL_LED_RF_STATE, \ + .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_A000, \ + .non_shared_ant = ANT_A, \ + .dccm_offset = IWL_A000_DCCM_OFFSET, \ + .dccm_len = IWL_A000_DCCM_LEN, \ + .dccm2_offset = IWL_A000_DCCM2_OFFSET, \ + .dccm2_len = IWL_A000_DCCM2_LEN, \ + .smem_offset = IWL_A000_SMEM_OFFSET, \ + .smem_len = IWL_A000_SMEM_LEN, \ + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \ + .apmg_not_supported = true, \ + .mq_rx_supported = true, \ + .vht_mu_mimo_supported = true, \ + .mac_addr_from_csr = true + +const struct iwl_cfg iwla000_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC a000", + .fw_name_pre = IWL_A000_FW_PRE, + IWL_DEVICE_A000, + .ht_params = &iwl_a000_ht_params, + .nvm_ver = IWL_A000_NVM_VERSION, + .nvm_calib_ver = IWL_A000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, +}; + +MODULE_FIRMWARE(IWL_A000_MODULE_FIRMWARE(IWL_A000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 57b14f4dcb89..122e895b52d2 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -261,6 +261,7 @@ struct iwl_tt_params { #define OTP_LOW_IMAGE_SIZE_FAMILY_7000 (16 * 512 * sizeof(u16)) /* 16 KB */ #define OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(u16)) /* 32 KB */ #define OTP_LOW_IMAGE_SIZE_FAMILY_9000 OTP_LOW_IMAGE_SIZE_FAMILY_8000 +#define OTP_LOW_IMAGE_SIZE_FAMILY_A000 OTP_LOW_IMAGE_SIZE_FAMILY_9000 struct iwl_eeprom_params { const u8 regulatory_bands[7]; @@ -450,6 +451,7 @@ extern const struct iwl_cfg iwl4165_2ac_sdio_cfg; extern const struct iwl_cfg iwl9260_2ac_cfg; extern const struct iwl_cfg iwl9260lc_2ac_cfg; extern const struct iwl_cfg iwl5165_2ac_cfg; +extern const struct iwl_cfg iwla000_2ac_cfg; #endif /* CONFIG_IWLMVM */ #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 6f020e4ec7dc..f70c3e2b1473 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -516,6 +516,9 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x2526, 0x1420, iwl5165_2ac_cfg)}, {IWL_PCI_DEVICE(0x9DF0, 0x0710, iwl5165_2ac_cfg)}, {IWL_PCI_DEVICE(0x9DF0, 0x2A10, iwl5165_2ac_cfg)}, + +/* a000 Series */ + {IWL_PCI_DEVICE(0x2720, 0x0A10, iwla000_2ac_cfg)}, #endif /* CONFIG_IWLMVM */ {0} From 2aabdbdc17b7c53490337bfc58de3409c84d85d2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 8 Jun 2016 23:07:31 +0300 Subject: [PATCH 28/56] iwlwifi: pcie: enable interrupts before releasing the NIC's CPU The NIC's CPU gets started after the firmware has been written to its memory. The first thing it does is to send an interrupt to let the driver know that it is running. In order to get that interrupt, the driver needs to make sure it is not masked. Of course, the interrupt needs to be enabled in the driver before the CPU starts to run. I mistakenly inversed those two steps leading to races which prevented the driver from getting the alive interrupt from the firmware. Fix that. Cc: [4.5+] Fixes: a6bd005fe92 ("iwlwifi: pcie: fix RF-Kill vs. firmware load race") Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 3badebb0febf..ac623c3e3f24 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -801,6 +801,8 @@ static int iwl_pcie_load_cpu_sections_8000(struct iwl_trans *trans, *first_ucode_section = last_read_idx; + iwl_enable_interrupts(trans); + if (cpu == 1) iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFF); else @@ -980,6 +982,8 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, iwl_pcie_apply_destination(trans); } + iwl_enable_interrupts(trans); + /* release CPU reset */ iwl_write32(trans, CSR_RESET, 0); @@ -1215,7 +1219,6 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, 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); From 849a9627299100ae3f0ce573fc87d2b476f3bb59 Mon Sep 17 00:00:00 2001 From: striebit Date: Tue, 7 Jun 2016 15:05:26 +0300 Subject: [PATCH 29/56] iwlmvm: mvm: set correct state in smart-fifo configuration Currently the state sent in SF configuration is always FULL_ON. This commit sets the correct state (e.g. INIT_OFF when station is not associated). Fixes: commit f4a3ee493e69 ("iwlwifi: mvm: Always enable the smart FIFO") Signed-off-by: Shaul Triebitz Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sf.c b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c index 443a42855c9e..101fb04a8573 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sf.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c @@ -215,7 +215,7 @@ static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id, enum iwl_sf_state new_state) { struct iwl_sf_cfg_cmd sf_cmd = { - .state = cpu_to_le32(SF_FULL_ON), + .state = cpu_to_le32(new_state), }; struct ieee80211_sta *sta; int ret = 0; From ecf51424152bad1b2727409f42ddf1bd86f44b7d Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 8 Jun 2016 15:15:41 +0300 Subject: [PATCH 30/56] iwlwifi: mvm: checksum IPv6 fragmented packet Our HW does not support checksum of fragmented packets. Fix code accordingly to checksum those packets in the driver. Signed-off-by: Sara Sharon Fixes: 5e6a98dc4863 ("iwlwifi: mvm: enable TCP/UDP checksum support for 9000 family") Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 2373142b0f43..f3a6e95cedec 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -138,28 +138,19 @@ static void iwl_mvm_tx_csum(struct iwl_mvm *mvm, struct sk_buff *skb, protocol = ipv6h->nexthdr; while (protocol != NEXTHDR_NONE && ipv6_ext_hdr(protocol)) { + struct ipv6_opt_hdr *hp; + /* only supported extension headers */ if (protocol != NEXTHDR_ROUTING && protocol != NEXTHDR_HOP && - protocol != NEXTHDR_DEST && - protocol != NEXTHDR_FRAGMENT) { + protocol != NEXTHDR_DEST) { skb_checksum_help(skb); return; } - if (protocol == NEXTHDR_FRAGMENT) { - struct frag_hdr *hp = - OPT_HDR(struct frag_hdr, skb, off); - - protocol = hp->nexthdr; - off += sizeof(struct frag_hdr); - } else { - struct ipv6_opt_hdr *hp = - OPT_HDR(struct ipv6_opt_hdr, skb, off); - - protocol = hp->nexthdr; - off += ipv6_optlen(hp); - } + hp = OPT_HDR(struct ipv6_opt_hdr, skb, off); + protocol = hp->nexthdr; + off += ipv6_optlen(hp); } /* if we get here - protocol now should be TCP/UDP */ #endif From 3aa4359fe1fbfe25be4b474d20094138d04a429a Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 13 Jun 2016 10:02:04 +0300 Subject: [PATCH 31/56] iwlwifi: mvm: cleanup the coex code We removed support for old API for coexistence, but we forgot to remove defines and variable that are not needed anymore. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- .../wireless/intel/iwlwifi/mvm/fw-api-coex.h | 222 ------------------ .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 4 - drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 5 - 3 files changed, 231 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h index 2a33b694ba10..204c1b13988b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h @@ -70,85 +70,6 @@ #define BITS(nb) (BIT(nb) - 1) -/** - * enum iwl_bt_coex_flags - flags for BT_COEX command - * @BT_COEX_MODE_POS: - * @BT_COEX_MODE_MSK: - * @BT_COEX_DISABLE_OLD: - * @BT_COEX_2W_OLD: - * @BT_COEX_3W_OLD: - * @BT_COEX_NW_OLD: - * @BT_COEX_AUTO_OLD: - * @BT_COEX_BT_OLD: Antenna is for BT (manufacuring tests) - * @BT_COEX_WIFI_OLD: Antenna is for BT (manufacuring tests) - * @BT_COEX_SYNC2SCO: - * @BT_COEX_CORUNNING: - * @BT_COEX_MPLUT: - * @BT_COEX_TTC: - * @BT_COEX_RRC: - * - * The COEX_MODE must be set for each command. Even if it is not changed. - */ -enum iwl_bt_coex_flags { - BT_COEX_MODE_POS = 3, - BT_COEX_MODE_MSK = BITS(3) << BT_COEX_MODE_POS, - BT_COEX_DISABLE_OLD = 0x0 << BT_COEX_MODE_POS, - BT_COEX_2W_OLD = 0x1 << BT_COEX_MODE_POS, - BT_COEX_3W_OLD = 0x2 << BT_COEX_MODE_POS, - BT_COEX_NW_OLD = 0x3 << BT_COEX_MODE_POS, - BT_COEX_AUTO_OLD = 0x5 << BT_COEX_MODE_POS, - BT_COEX_BT_OLD = 0x6 << BT_COEX_MODE_POS, - BT_COEX_WIFI_OLD = 0x7 << BT_COEX_MODE_POS, - BT_COEX_SYNC2SCO = BIT(7), - BT_COEX_CORUNNING = BIT(8), - BT_COEX_MPLUT = BIT(9), - BT_COEX_TTC = BIT(20), - BT_COEX_RRC = BIT(21), -}; - -/* - * indicates what has changed in the BT_COEX command. - * BT_VALID_ENABLE must be set for each command. Commands without this bit will - * discarded by the firmware - */ -enum iwl_bt_coex_valid_bit_msk { - BT_VALID_ENABLE = BIT(0), - BT_VALID_BT_PRIO_BOOST = BIT(1), - BT_VALID_MAX_KILL = BIT(2), - BT_VALID_3W_TMRS = BIT(3), - BT_VALID_KILL_ACK = BIT(4), - BT_VALID_KILL_CTS = BIT(5), - BT_VALID_REDUCED_TX_POWER = BIT(6), - BT_VALID_LUT = BIT(7), - BT_VALID_WIFI_RX_SW_PRIO_BOOST = BIT(8), - BT_VALID_WIFI_TX_SW_PRIO_BOOST = BIT(9), - BT_VALID_MULTI_PRIO_LUT = BIT(10), - BT_VALID_TRM_KICK_FILTER = BIT(11), - BT_VALID_CORUN_LUT_20 = BIT(12), - BT_VALID_CORUN_LUT_40 = BIT(13), - BT_VALID_ANT_ISOLATION = BIT(14), - BT_VALID_ANT_ISOLATION_THRS = BIT(15), - BT_VALID_TXTX_DELTA_FREQ_THRS = BIT(16), - BT_VALID_TXRX_MAX_FREQ_0 = BIT(17), - BT_VALID_SYNC_TO_SCO = BIT(18), - BT_VALID_TTC = BIT(20), - BT_VALID_RRC = BIT(21), -}; - -/** - * enum iwl_bt_reduced_tx_power - allows to reduce txpower for WiFi frames. - * @BT_REDUCED_TX_POWER_CTL: reduce Tx power for control frames - * @BT_REDUCED_TX_POWER_DATA: reduce Tx power for data frames - * - * This mechanism allows to have BT and WiFi run concurrently. Since WiFi - * reduces its Tx power, it can work along with BT, hence reducing the amount - * of WiFi frames being killed by BT. - */ -enum iwl_bt_reduced_tx_power { - BT_REDUCED_TX_POWER_CTL = BIT(0), - BT_REDUCED_TX_POWER_DATA = BIT(1), -}; - enum iwl_bt_coex_lut_type { BT_COEX_TIGHT_LUT = 0, BT_COEX_LOOSE_LUT, @@ -158,64 +79,9 @@ enum iwl_bt_coex_lut_type { BT_COEX_INVALID_LUT = 0xff, }; /* BT_COEX_DECISION_LUT_INDEX_API_E_VER_1 */ -#define BT_COEX_LUT_SIZE (12) #define BT_COEX_CORUN_LUT_SIZE (32) -#define BT_COEX_MULTI_PRIO_LUT_SIZE (2) -#define BT_COEX_BOOST_SIZE (4) #define BT_REDUCED_TX_POWER_BIT BIT(7) -/** - * struct iwl_bt_coex_cmd_old - bt coex configuration command - * @flags:&enum iwl_bt_coex_flags - * @max_kill: - * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power - * @override_primary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT - * should be set by default - * @override_secondary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT - * should be set by default - * @bt4_antenna_isolation: antenna isolation - * @bt4_antenna_isolation_thr: antenna threshold value - * @bt4_tx_tx_delta_freq_thr: TxTx delta frequency - * @bt4_tx_rx_max_freq0: TxRx max frequency - * @bt_prio_boost: BT priority boost registers - * @wifi_tx_prio_boost: SW boost of wifi tx priority - * @wifi_rx_prio_boost: SW boost of wifi rx priority - * @kill_ack_msk: kill ACK mask. 1 - Tx ACK, 0 - kill Tx of ACK. - * @kill_cts_msk: kill CTS mask. 1 - Tx CTS, 0 - kill Tx of CTS. - * @decision_lut: PTA decision LUT, per Prio-Ch - * @bt4_multiprio_lut: multi priority LUT configuration - * @bt4_corun_lut20: co-running 20 MHz LUT configuration - * @bt4_corun_lut40: co-running 40 MHz LUT configuration - * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk - * - * The structure is used for the BT_COEX command. - */ -struct iwl_bt_coex_cmd_old { - __le32 flags; - u8 max_kill; - u8 bt_reduced_tx_power; - u8 override_primary_lut; - u8 override_secondary_lut; - - u8 bt4_antenna_isolation; - u8 bt4_antenna_isolation_thr; - u8 bt4_tx_tx_delta_freq_thr; - u8 bt4_tx_rx_max_freq0; - - __le32 bt_prio_boost[BT_COEX_BOOST_SIZE]; - __le32 wifi_tx_prio_boost; - __le32 wifi_rx_prio_boost; - __le32 kill_ack_msk; - __le32 kill_cts_msk; - - __le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE]; - __le32 bt4_multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE]; - __le32 bt4_corun_lut20[BT_COEX_CORUN_LUT_SIZE]; - __le32 bt4_corun_lut40[BT_COEX_CORUN_LUT_SIZE]; - - __le32 valid_bit_msk; -} __packed; /* BT_COEX_CMD_API_S_VER_5 */ - enum iwl_bt_coex_mode { BT_COEX_DISABLE = 0x0, BT_COEX_NW = 0x1, @@ -385,92 +251,4 @@ struct iwl_bt_coex_profile_notif { u8 reserved[3]; } __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */ -enum iwl_bt_coex_prio_table_event { - BT_COEX_PRIO_TBL_EVT_INIT_CALIB1 = 0, - BT_COEX_PRIO_TBL_EVT_INIT_CALIB2 = 1, - BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1 = 2, - BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2 = 3, - BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1 = 4, - BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2 = 5, - BT_COEX_PRIO_TBL_EVT_DTIM = 6, - BT_COEX_PRIO_TBL_EVT_SCAN52 = 7, - BT_COEX_PRIO_TBL_EVT_SCAN24 = 8, - BT_COEX_PRIO_TBL_EVT_IDLE = 9, - BT_COEX_PRIO_TBL_EVT_MAX = 16, -}; /* BT_COEX_PRIO_TABLE_EVENTS_API_E_VER_1 */ - -enum iwl_bt_coex_prio_table_prio { - BT_COEX_PRIO_TBL_DISABLED = 0, - BT_COEX_PRIO_TBL_PRIO_LOW = 1, - BT_COEX_PRIO_TBL_PRIO_HIGH = 2, - BT_COEX_PRIO_TBL_PRIO_BYPASS = 3, - BT_COEX_PRIO_TBL_PRIO_COEX_OFF = 4, - BT_COEX_PRIO_TBL_PRIO_COEX_ON = 5, - BT_COEX_PRIO_TBL_PRIO_COEX_IDLE = 6, - BT_COEX_PRIO_TBL_MAX = 8, -}; /* BT_COEX_PRIO_TABLE_PRIORITIES_API_E_VER_1 */ - -#define BT_COEX_PRIO_TBL_SHRD_ANT_POS (0) -#define BT_COEX_PRIO_TBL_PRIO_POS (1) -#define BT_COEX_PRIO_TBL_RESERVED_POS (4) - -/** - * struct iwl_bt_coex_prio_tbl_cmd - priority table for BT coex - * @prio_tbl: - */ -struct iwl_bt_coex_prio_tbl_cmd { - u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX]; -} __packed; - -/** - * struct iwl_bt_coex_ci_cmd_old - bt coex channel inhibition command - * @bt_primary_ci: - * @bt_secondary_ci: - * @co_run_bw_primary: - * @co_run_bw_secondary: - * @primary_ch_phy_id: - * @secondary_ch_phy_id: - * - * Used for BT_COEX_CI command - */ -struct iwl_bt_coex_ci_cmd_old { - __le64 bt_primary_ci; - __le64 bt_secondary_ci; - - u8 co_run_bw_primary; - u8 co_run_bw_secondary; - u8 primary_ch_phy_id; - u8 secondary_ch_phy_id; -} __packed; /* BT_CI_MSG_API_S_VER_1 */ - -/** - * struct iwl_bt_coex_profile_notif_old - notification about BT coex - * @mbox_msg: message from BT to WiFi - * @msg_idx: the index of the message - * @bt_status: 0 - off, 1 - on - * @bt_open_conn: number of BT connections open - * @bt_traffic_load: load of BT traffic - * @bt_agg_traffic_load: aggregated load of BT traffic - * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant - * @primary_ch_lut: LUT used for primary channel - * @secondary_ch_lut: LUT used for secondary channel - * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading - */ -struct iwl_bt_coex_profile_notif_old { - __le32 mbox_msg[4]; - __le32 msg_idx; - u8 bt_status; - u8 bt_open_conn; - u8 bt_traffic_load; - u8 bt_agg_traffic_load; - u8 bt_ci_compliance; - u8 ttc_enabled; - u8 rrc_enabled; - u8 reserved; - - __le32 primary_ch_lut; - __le32 secondary_ch_lut; - __le32 bt_activity_grading; -} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */ - #endif /* __fw_api_bt_coex_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 5c044eac1eed..e44c337e604e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1020,11 +1020,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) memset(mvm->sta_deferred_frames, 0, sizeof(mvm->sta_deferred_frames)); memset(mvm->tfd_drained, 0, sizeof(mvm->tfd_drained)); memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); - memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old)); memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd)); - memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old)); - memset(&mvm->bt_ack_kill_msk, 0, sizeof(mvm->bt_ack_kill_msk)); - memset(&mvm->bt_cts_kill_msk, 0, sizeof(mvm->bt_cts_kill_msk)); ieee80211_wake_queues(mvm->hw); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index e240f198e001..c440b10b3cde 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -932,11 +932,6 @@ struct iwl_mvm { wait_queue_head_t d0i3_exit_waitq; /* BT-Coex */ - u8 bt_ack_kill_msk[NUM_PHY_CTX]; - u8 bt_cts_kill_msk[NUM_PHY_CTX]; - - struct iwl_bt_coex_profile_notif_old last_bt_notif_old; - struct iwl_bt_coex_ci_cmd_old last_bt_ci_cmd_old; struct iwl_bt_coex_profile_notif last_bt_notif; struct iwl_bt_coex_ci_cmd last_bt_ci_cmd; From da2830acf15aab9cb2c376b74463d6d6ecc18716 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Mon, 30 May 2016 13:00:44 +0300 Subject: [PATCH 32/56] iwlwifi: mvm: read SAR BIOS table from ACPI Read the SAR BIOS table from the ACPI and parse it into the iwl_mvm_sar_table structure. If the table is enabled, send it to the firmware via REDUCE_TX_POWER_CMD. Signed-off-by: Luca Coelho Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 173 ++++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 510b4c1be35d..4db1d84749ab 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -65,6 +65,7 @@ *****************************************************************************/ #include #include +#include #include "iwl-trans.h" #include "iwl-op-mode.h" @@ -902,6 +903,174 @@ static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) sizeof(cmd), &cmd); } +#define ACPI_WRDS_METHOD "WRDS" +#define ACPI_WRDS_WIFI (0x07) +#define ACPI_WRDS_TABLE_SIZE 10 + +struct iwl_mvm_sar_table { + bool enabled; + u8 values[ACPI_WRDS_TABLE_SIZE]; +}; + +#ifdef CONFIG_ACPI +static int iwl_mvm_sar_get_wrds(struct iwl_mvm *mvm, union acpi_object *wrds, + struct iwl_mvm_sar_table *sar_table) +{ + union acpi_object *data_pkg; + u32 i; + + /* We need at least two packages, one for the revision and one + * for the data itself. Also check that the revision is valid + * (i.e. it is an integer set to 0). + */ + if (wrds->type != ACPI_TYPE_PACKAGE || + wrds->package.count < 2 || + wrds->package.elements[0].type != ACPI_TYPE_INTEGER || + wrds->package.elements[0].integer.value != 0) { + IWL_DEBUG_RADIO(mvm, "Unsupported wrds structure\n"); + return -EINVAL; + } + + /* loop through all the packages to find the one for WiFi */ + for (i = 1; i < wrds->package.count; i++) { + union acpi_object *domain; + + data_pkg = &wrds->package.elements[i]; + + /* Skip anything that is not a package with the right + * amount of elements (i.e. domain_type, + * enabled/disabled plus the sar table size. + */ + if (data_pkg->type != ACPI_TYPE_PACKAGE || + data_pkg->package.count != ACPI_WRDS_TABLE_SIZE + 2) + continue; + + domain = &data_pkg->package.elements[0]; + if (domain->type == ACPI_TYPE_INTEGER && + domain->integer.value == ACPI_WRDS_WIFI) + break; + + data_pkg = NULL; + } + + if (!data_pkg) + return -ENOENT; + + if (data_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) + return -EINVAL; + + sar_table->enabled = !!(data_pkg->package.elements[1].integer.value); + + for (i = 0; i < ACPI_WRDS_TABLE_SIZE; i++) { + union acpi_object *entry; + + entry = &data_pkg->package.elements[i + 2]; + if ((entry->type != ACPI_TYPE_INTEGER) || + (entry->integer.value > U8_MAX)) + return -EINVAL; + + sar_table->values[i] = entry->integer.value; + } + + return 0; +} + +static int iwl_mvm_sar_get_table(struct iwl_mvm *mvm, + struct iwl_mvm_sar_table *sar_table) +{ + acpi_handle root_handle; + acpi_handle handle; + struct acpi_buffer wrds = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_status status; + int ret; + + root_handle = ACPI_HANDLE(mvm->dev); + if (!root_handle) { + IWL_DEBUG_RADIO(mvm, + "Could not retrieve root port ACPI handle\n"); + return -ENOENT; + } + + /* Get the method's handle */ + status = acpi_get_handle(root_handle, (acpi_string)ACPI_WRDS_METHOD, + &handle); + if (ACPI_FAILURE(status)) { + IWL_DEBUG_RADIO(mvm, "WRDS method not found\n"); + return -ENOENT; + } + + /* Call WRDS with no arguments */ + status = acpi_evaluate_object(handle, NULL, NULL, &wrds); + if (ACPI_FAILURE(status)) { + IWL_DEBUG_RADIO(mvm, "WRDS invocation failed (0x%x)\n", status); + return -ENOENT; + } + + ret = iwl_mvm_sar_get_wrds(mvm, wrds.pointer, sar_table); + kfree(wrds.pointer); + + return ret; +} +#else /* CONFIG_ACPI */ +static int iwl_mvm_sar_get_table(struct iwl_mvm *mvm, + struct iwl_mvm_sar_table *sar_table) +{ + return -ENOENT; +} +#endif /* CONFIG_ACPI */ + +static int iwl_mvm_sar_init(struct iwl_mvm *mvm) +{ + struct iwl_mvm_sar_table sar_table; + struct iwl_dev_tx_power_cmd cmd = { + .v2.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS), + }; + int ret, i, j, idx; + + /* we can't do anything with the table if the FW doesn't support it */ + if (!fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_TX_POWER_CHAIN)) { + IWL_DEBUG_RADIO(mvm, + "FW doesn't support per-chain TX power settings.\n"); + return 0; + } + + ret = iwl_mvm_sar_get_table(mvm, &sar_table); + if (ret < 0) { + IWL_DEBUG_RADIO(mvm, + "SAR BIOS table invalid or unavailable. (%d)\n", + ret); + /* we don't fail if the table is not available */ + return 0; + } + + if (!sar_table.enabled) + return 0; + + IWL_DEBUG_RADIO(mvm, "Sending REDUCE_TX_POWER_CMD per chain\n"); + + BUILD_BUG_ON(IWL_NUM_CHAIN_LIMITS * IWL_NUM_SUB_BANDS != + ACPI_WRDS_TABLE_SIZE); + + for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { + IWL_DEBUG_RADIO(mvm, " Chain[%d]:\n", i); + for (j = 0; j < IWL_NUM_SUB_BANDS; j++) { + idx = (i * IWL_NUM_SUB_BANDS) + j; + cmd.per_chain_restriction[i][j] = + cpu_to_le16(sar_table.values[idx]); + IWL_DEBUG_RADIO(mvm, " Band[%d] = %d * .125dBm\n", + j, sar_table.values[idx]); + } + } + + ret = iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, + sizeof(cmd), &cmd); + if (ret) + IWL_ERR(mvm, "failed to set per-chain TX power: %d\n", ret); + + return ret; +} + int iwl_mvm_up(struct iwl_mvm *mvm) { int ret, i; @@ -1077,6 +1246,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); + ret = iwl_mvm_sar_init(mvm); + if (ret) + goto error; + IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); return 0; error: From 54f315cb082bf98290575e858d1e0274bb0eebe0 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Tue, 14 Jun 2016 10:27:57 -0400 Subject: [PATCH 33/56] iwlwifi: pcie: Enable MSI mode when using MSI interrupts On some of the chipsets MSI & INTA interrupts are disabled by default in the HW registers, and need to be explicitly enabled to be used. In case MSI-X isn't used, make sure MSI mode is enabled by setting the relevant HW register. Signed-off-by: Ido Yariv Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 1 + drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 6c1d20ded04b..459bf736fd5b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -417,5 +417,6 @@ enum { }; #define UREG_CHICK (0xA05C00) +#define UREG_CHICK_MSI_ENABLE BIT(24) #define UREG_CHICK_MSIX_ENABLE BIT(25) #endif /* __iwl_prph_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index ac623c3e3f24..f5ace924e76a 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1396,8 +1396,12 @@ static void iwl_pcie_init_msix(struct iwl_trans_pcie *trans_pcie) max_rx_vector = trans_pcie->allocated_vector - 1; - if (!trans_pcie->msix_enabled) + if (!trans_pcie->msix_enabled) { + if (trans->cfg->mq_rx_supported) + iwl_write_prph(trans, UREG_CHICK, + UREG_CHICK_MSI_ENABLE); return; + } iwl_write_prph(trans, UREG_CHICK, UREG_CHICK_MSIX_ENABLE); From d5d0689aefc59c6a5352ca25d7e6d47d03f543ce Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 9 Jun 2016 17:19:35 +0300 Subject: [PATCH 34/56] iwlwifi: pcie: fix access to scratch buffer This fixes a pretty ancient bug that hasn't manifested itself until now. The scratchbuf for command queue is allocated only for 32 slots but is accessed with the queue write pointer - which can be up to 256. Since the scratch buf size was 16 and there are up to 256 TFDs we never passed a page boundary when accessing the scratch buffer, but when attempting to increase the size of the scratch buffer a panic was quick to follow when trying to access the address resulted in a page boundary. Signed-off-by: Sara Sharon Fixes: 38c0f334b359 ("iwlwifi: use coherent DMA memory for command header") Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 8901d49aebd5..9b5858bc8ee2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -1604,9 +1604,9 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, /* start the TFD with the scratchbuf */ scratch_size = min_t(int, copy_size, IWL_HCMD_SCRATCHBUF_SIZE); - memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size); + memcpy(&txq->scratchbufs[idx], &out_cmd->hdr, scratch_size); iwl_pcie_txq_build_tfd(trans, txq, - iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr), + iwl_pcie_get_scratchbuf_dma(txq, idx), scratch_size, true); /* map first command fragment, if any remains */ From e7c9bd1cc632e924a69bf704385484386bb10933 Mon Sep 17 00:00:00 2001 From: Golan Ben-Ami Date: Wed, 15 Jun 2016 09:16:24 +0300 Subject: [PATCH 35/56] iwlwifi: mvm: write the correct internal TXF index The TX fifos are arranged consecutively in the SMEM, beginning with the regular fifos, and tailed by the internal fifos. In the current code, while trying to read the internal fifos, we read the fifos beginning with the index zero. By doing this we actually re-read the regular fifos. In order to read the internal fifos, start the reading index from the number of regular fifos configured by the fw. Signed-off-by: Golan Ben-Ami Fixes: 39654cb3a6a2 ("iwlwifi: don't access a nonexistent register upon assert") Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index 1ece19dcd9ac..6e692a8f5cda 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -288,7 +288,8 @@ static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, fifo_hdr->fifo_num = cpu_to_le32(i); /* Mark the number of TXF we're pulling now */ - iwl_trans_write_prph(mvm->trans, TXF_CPU2_NUM, i); + iwl_trans_write_prph(mvm->trans, TXF_CPU2_NUM, i + + ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size)); fifo_hdr->available_bytes = cpu_to_le32(iwl_trans_read_prph(mvm->trans, From c934bce9de4b715b74e376a904e4426def586314 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 20 Jun 2016 09:40:40 +0300 Subject: [PATCH 36/56] iwlwifi: mvm: fix coex related comments Those comments were wrong, fix them. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/coex.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index a63f5bbb1ba7..07fc9817c906 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -615,8 +615,8 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, * don't reduce the Tx power if one of these is true: * we are in LOOSE * single share antenna product - * BT is active - * we are associated + * BT is inactive + * we are not associated */ if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT || mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc || From ad17b1d9536594c6f4b52a99e9efbf1050bf99d9 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 22 Jun 2016 10:51:31 +0300 Subject: [PATCH 37/56] iwlwifi: mvm: fix the channel inhibition table for Channel 14 The value for Channel 14 was wrong. Fix it. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/coex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index 07fc9817c906..5bdb6c2c8390 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -142,7 +142,7 @@ static const __le64 iwl_ci_mask[][3] = { cpu_to_le64(0x0) }, { - cpu_to_le64(0xFFC0000000ULL), + cpu_to_le64(0xFE00000000ULL), cpu_to_le64(0x0ULL), cpu_to_le64(0x0ULL) }, From 5e7d7eb9cc553bef0d1386bb736f8d0a6152dc21 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 21 Jun 2016 12:23:35 +0200 Subject: [PATCH 38/56] iwlwifi: remove iwl_ht_params.smps_mode This struct member is never set, so remove it. Since this is the last thing that needs mac80211.h, also change the includes to no longer use mac80211.h Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/dvm/rxon.c | 5 ----- drivers/net/wireless/intel/iwlwifi/iwl-config.h | 6 +++--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c b/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c index b228552184b5..087e579854ab 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c @@ -523,11 +523,6 @@ static int iwlagn_rxon_connect(struct iwl_priv *priv, return ret; } - if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION && - priv->cfg->ht_params && priv->cfg->ht_params->smps_mode) - ieee80211_request_smps(ctx->vif, - priv->cfg->ht_params->smps_mode); - return 0; } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 122e895b52d2..64108cc975cf 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -66,8 +66,9 @@ #define __IWL_CONFIG_H__ #include -#include - +#include +#include +#include enum iwl_device_family { IWL_DEVICE_FAMILY_UNDEFINED, @@ -192,7 +193,6 @@ struct iwl_base_params { * @ht40_bands: bitmap of bands (using %NL80211_BAND_*) that support HT40 */ struct iwl_ht_params { - enum ieee80211_smps_mode smps_mode; u8 ht_greenfield_support:1, stbc:1, ldpc:1, From 3edbc7dabab8ce85aa75c5e290ecda7a3692ebc9 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 19 Jun 2016 20:57:02 +0300 Subject: [PATCH 39/56] iwlwifi: mvm: unmap the paging memory before freeing it This led to a DMA splat. Fixes: a6c4fb4441f4 ("iwlwifi: mvm: Add FW paging mechanism for the UMAC on PCI") Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 4db1d84749ab..4c16fa79b462 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -160,17 +160,21 @@ void iwl_free_fw_paging(struct iwl_mvm *mvm) return; for (i = 0; i < NUM_OF_FW_PAGING_BLOCKS; i++) { - if (!mvm->fw_paging_db[i].fw_paging_block) { + struct iwl_fw_paging *paging = &mvm->fw_paging_db[i]; + + if (!paging->fw_paging_block) { IWL_DEBUG_FW(mvm, "Paging: block %d already freed, continue to next page\n", i); continue; } + dma_unmap_page(mvm->trans->dev, paging->fw_paging_phys, + paging->fw_paging_size, DMA_BIDIRECTIONAL); - __free_pages(mvm->fw_paging_db[i].fw_paging_block, - get_order(mvm->fw_paging_db[i].fw_paging_size)); - mvm->fw_paging_db[i].fw_paging_block = NULL; + __free_pages(paging->fw_paging_block, + get_order(paging->fw_paging_size)); + paging->fw_paging_block = NULL; } kfree(mvm->trans->paging_download_buf); mvm->trans->paging_download_buf = NULL; From e25d65f2670adfcbb019f7fee4302a60333f913a Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 21 Jun 2016 11:13:47 +0300 Subject: [PATCH 40/56] iwlwifi: pcie: don't use vid 0 In cases of hardware or DMA error, the vid read from a zeroed location will be 0, and we will access the rxb at index 0 in the global table, while it may be NULL or owned by hardware. Invalidate vid 0 in order to detect the situation and bail out. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index c1c3c6a86e59..0296c290f3a1 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -960,7 +960,7 @@ int iwl_pcie_rx_init(struct iwl_trans *trans) else list_add(&rxb->list, &def_rxq->rx_used); trans_pcie->global_table[i] = rxb; - rxb->vid = (u16)i; + rxb->vid = (u16)(i + 1); } iwl_pcie_rxq_alloc_rbs(trans, GFP_KERNEL, def_rxq); @@ -1249,10 +1249,13 @@ static void iwl_pcie_rx_handle(struct iwl_trans *trans, int queue) */ u16 vid = le32_to_cpu(rxq->used_bd[i]) & 0x0FFF; - if (WARN(vid >= ARRAY_SIZE(trans_pcie->global_table), - "Invalid rxb index from HW %u\n", (u32)vid)) + if (WARN(!vid || + vid > ARRAY_SIZE(trans_pcie->global_table), + "Invalid rxb index from HW %u\n", (u32)vid)) { + iwl_force_nmi(trans); goto out; - rxb = trans_pcie->global_table[vid]; + } + rxb = trans_pcie->global_table[vid - 1]; } else { rxb = rxq->queue[i]; rxq->queue[i] = NULL; From e3118ad74d7ef5bbf1a4a17471466e2cafacc752 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Sun, 5 Jun 2016 10:49:02 +0300 Subject: [PATCH 41/56] iwlwifi: mvm: support tdls in dqa mode Support TDLS when working in DQA mode. This is done mainly by NOT doing any special things for TDLS, as the queues are dynamically created anyway, so no need to allocate them ahead of time. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 31 ++++++++++++-------- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 8 ++++- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 7321bb6b8d67..1617de7c2ddc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -893,8 +893,11 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, mvm_sta->tid_disable_agg = 0xffff; /* No aggs at first */ mvm_sta->tfd_queue_msk = 0; - /* allocate new queues for a TDLS station */ - if (sta->tdls) { + /* + * Allocate new queues for a TDLS station, unless we're in DQA mode, + * and then they'll be allocated dynamically + */ + if (!iwl_mvm_is_dqa_supported(mvm) && sta->tdls) { ret = iwl_mvm_tdls_sta_init(mvm, sta); if (ret) return ret; @@ -958,7 +961,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, return 0; err: - iwl_mvm_tdls_sta_deinit(mvm, sta); + if (!iwl_mvm_is_dqa_supported(mvm) && sta->tdls) + iwl_mvm_tdls_sta_deinit(mvm, sta); return ret; } @@ -1164,16 +1168,19 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, if (iwl_mvm_is_dqa_supported(mvm)) iwl_mvm_disable_sta_queues(mvm, vif, mvm_sta); - /* if we are associated - we can't remove the AP STA now */ - if (vif->bss_conf.assoc) - return ret; + if (vif->type == NL80211_IFTYPE_STATION && + mvmvif->ap_sta_id == mvm_sta->sta_id) { + /* if associated - we can't remove the AP STA now */ + if (vif->bss_conf.assoc) + return ret; - /* unassoc - go ahead - remove the AP STA now */ - mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; + /* unassoc - go ahead - remove the AP STA now */ + mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; - /* clear d0i3_ap_sta_id if no longer relevant */ - if (mvm->d0i3_ap_sta_id == mvm_sta->sta_id) - mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; + /* clear d0i3_ap_sta_id if no longer relevant */ + if (mvm->d0i3_ap_sta_id == mvm_sta->sta_id) + mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; + } } /* @@ -1211,7 +1218,7 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, } else { spin_unlock_bh(&mvm_sta->lock); - if (sta->tdls) + if (!iwl_mvm_is_dqa_supported(mvm) && sta->tdls) iwl_mvm_tdls_sta_deinit(mvm, sta); ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index f3a6e95cedec..c6585ab48df3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -546,6 +546,9 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) * (this is not possible for unicast packets as a TLDS discovery * response are sent without a station entry); otherwise use the * AUX station. + * In DQA mode, if vif is of type STATION and frames are not multicast, + * they should be sent from the BSS queue. For example, TDLS setup + * frames should be sent on this queue, as they go through the AP. */ sta_id = mvm->aux_sta.sta_id; if (info.control.vif) { @@ -563,6 +566,9 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) if (ap_sta_id != IWL_MVM_STATION_COUNT) sta_id = ap_sta_id; + } else if (iwl_mvm_is_dqa_supported(mvm) && + info.control.vif->type == NL80211_IFTYPE_STATION) { + queue = IWL_MVM_DQA_BSS_CLIENT_QUEUE; } } @@ -906,7 +912,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM); - if (sta->tdls) { + if (sta->tdls && !iwl_mvm_is_dqa_supported(mvm)) { /* default to TID 0 for non-QoS packets */ u8 tdls_tid = tid == IWL_MAX_TID_COUNT ? 0 : tid; From 58f2cc57dc6ac89946757b812fc5b93157978c37 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Wed, 30 Sep 2015 16:44:28 +0200 Subject: [PATCH 42/56] iwlwifi: mvm: support dqa-mode scd queue redirection Make sure that in DQA mode, the SCD's configuration of a queue is redirected to the lower AC of the streams of the queue. Make sure that this queue is redirected to the lowest AC when adding a new RA/TID to an existing queue. If it isn't - redirect the queue. Also, as redirection revealed a bug in the marking of a shared queue, this patch contains a small fix to make sure a shared queue maintains the appropriate "shared queue marking". Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 115 ++++++++++++++++++- 1 file changed, 110 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 1617de7c2ddc..a6d6df9529d0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -513,6 +513,101 @@ static int iwl_mvm_get_shared_queue(struct iwl_mvm *mvm, return queue; } +/* + * If a given queue has a higher AC than the TID stream that is being added to + * it, the queue needs to be redirected to the lower AC. This function does that + * in such a case, otherwise - if no redirection required - it does nothing, + * unless the %force param is true. + */ +static int iwl_mvm_scd_queue_redirect(struct iwl_mvm *mvm, int queue, int tid, + int ac, int ssn, unsigned int wdg_timeout, + bool force) +{ + struct iwl_scd_txq_cfg_cmd cmd = { + .scd_queue = queue, + .enable = 0, + }; + bool shared_queue; + unsigned long mq; + int ret; + + /* + * If the AC is lower than current one - FIFO needs to be redirected to + * the lowest one of the streams in the queue. Check if this is needed + * here. + * Notice that the enum ieee80211_ac_numbers is "flipped", so BK is with + * value 3 and VO with value 0, so to check if ac X is lower than ac Y + * we need to check if the numerical value of X is LARGER than of Y. + */ + spin_lock_bh(&mvm->queue_info_lock); + if (ac <= mvm->queue_info[queue].mac80211_ac && !force) { + spin_unlock_bh(&mvm->queue_info_lock); + + IWL_DEBUG_TX_QUEUES(mvm, + "No redirection needed on TXQ #%d\n", + queue); + return 0; + } + + cmd.sta_id = mvm->queue_info[queue].ra_sta_id; + cmd.tx_fifo = iwl_mvm_ac_to_tx_fifo[mvm->queue_info[queue].mac80211_ac]; + mq = mvm->queue_info[queue].hw_queue_to_mac80211; + shared_queue = (mvm->queue_info[queue].hw_queue_refcount > 1); + spin_unlock_bh(&mvm->queue_info_lock); + + IWL_DEBUG_TX_QUEUES(mvm, "Redirecting shared TXQ #%d to FIFO #%d\n", + queue, iwl_mvm_ac_to_tx_fifo[ac]); + + /* Stop MAC queues and wait for this queue to empty */ + iwl_mvm_stop_mac_queues(mvm, mq); + ret = iwl_trans_wait_tx_queue_empty(mvm->trans, BIT(queue)); + if (ret) { + IWL_ERR(mvm, "Error draining queue %d before reconfig\n", + queue); + ret = -EIO; + goto out; + } + + /* Before redirecting the queue we need to de-activate it */ + iwl_trans_txq_disable(mvm->trans, queue, false); + ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd); + if (ret) + IWL_ERR(mvm, "Failed SCD disable TXQ %d (ret=%d)\n", queue, + ret); + + /* Make sure the SCD wrptr is correctly set before reconfiguring */ + iwl_trans_txq_enable(mvm->trans, queue, iwl_mvm_ac_to_tx_fifo[ac], + cmd.sta_id, tid, LINK_QUAL_AGG_FRAME_LIMIT_DEF, + ssn, wdg_timeout); + + /* TODO: Work-around SCD bug when moving back by multiples of 0x40 */ + + /* Redirect to lower AC */ + iwl_mvm_reconfig_scd(mvm, queue, iwl_mvm_ac_to_tx_fifo[ac], + cmd.sta_id, tid, LINK_QUAL_AGG_FRAME_LIMIT_DEF, + ssn); + + /* Update AC marking of the queue */ + spin_lock_bh(&mvm->queue_info_lock); + mvm->queue_info[queue].mac80211_ac = ac; + spin_unlock_bh(&mvm->queue_info_lock); + + /* + * Mark queue as shared in transport if shared + * Note this has to be done after queue enablement because enablement + * can also set this value, and there is no indication there to shared + * queues + */ + if (shared_queue) + iwl_trans_txq_set_shared_mode(mvm->trans, queue, true); + +out: + /* Continue using the MAC queues */ + iwl_mvm_start_mac_queues(mvm, mq); + + return ret; +} + static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, struct ieee80211_sta *sta, u8 ac, int tid, struct ieee80211_hdr *hdr) @@ -680,17 +775,21 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, iwl_mvm_invalidate_sta_queue(mvm, queue, disable_agg_tids, false); } - - /* Mark queue as shared in transport */ - iwl_trans_txq_set_shared_mode(mvm->trans, queue, true); - - /* TODO: a redirection may be required - DQA phase 2 */ } ssn = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); iwl_mvm_enable_txq(mvm, queue, mac_queue, ssn, &cfg, wdg_timeout); + /* + * Mark queue as shared in transport if shared + * Note this has to be done after queue enablement because enablement + * can also set this value, and there is no indication there to shared + * queues + */ + if (shared_queue) + iwl_trans_txq_set_shared_mode(mvm->trans, queue, true); + spin_lock_bh(&mvmsta->lock); mvmsta->tid_data[tid].txq_id = queue; mvmsta->tid_data[tid].is_tid_active = true; @@ -712,6 +811,12 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, if (ret) goto out_err; } + } else { + /* Redirect queue, if needed */ + ret = iwl_mvm_scd_queue_redirect(mvm, queue, tid, ac, ssn, + wdg_timeout, false); + if (ret) + goto out_err; } return 0; From 35263a03115726dcd393bc96504adbdd6ef08216 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 21 Jun 2016 12:12:10 +0300 Subject: [PATCH 43/56] iwlwifi: mvm: add RX aggregation prints Add some prints to track BAID assignment. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 6 ++++++ drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 7 +++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index d13397a17365..a1cbf489ec50 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -476,6 +476,9 @@ void iwl_mvm_reorder_timer_expired(unsigned long data) rcu_read_lock(); sta = rcu_dereference(buf->mvm->fw_id_to_mac_id[buf->sta_id]); /* SN is set to the last expired frame + 1 */ + IWL_DEBUG_HT(buf->mvm, + "Releasing expired frames for sta %u, sn %d\n", + buf->sta_id, sn); iwl_mvm_release_frames(buf->mvm, sta, NULL, buf, sn); rcu_read_unlock(); } else if (buf->num_stored) { @@ -956,6 +959,9 @@ void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi, int baid = release->baid; + IWL_DEBUG_HT(mvm, "Frame release notification for BAID %u, NSSN %d\n", + release->baid, le16_to_cpu(release->nssn)); + if (WARN_ON_ONCE(baid == IWL_RX_REORDER_DATA_INVALID_BAID)) return; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index a6d6df9529d0..c0c2ea344ad4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1768,8 +1768,8 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, switch (status & IWL_ADD_STA_STATUS_MASK) { case ADD_STA_SUCCESS: - IWL_DEBUG_INFO(mvm, "RX BA Session %sed in fw\n", - start ? "start" : "stopp"); + IWL_DEBUG_HT(mvm, "RX BA Session %sed in fw\n", + start ? "start" : "stopp"); break; case ADD_STA_IMMEDIATE_BA_FAILURE: IWL_WARN(mvm, "RX BA Session refused by fw\n"); @@ -1824,6 +1824,8 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, * supposed to happen) and we will free the session data while * RX is being processed in parallel */ + IWL_DEBUG_HT(mvm, "Sta %d(%d) is assigned to BAID %d\n", + mvm_sta->sta_id, tid, baid); WARN_ON(rcu_access_pointer(mvm->baid_map[baid])); rcu_assign_pointer(mvm->baid_map[baid], baid_data); } else if (mvm->rx_ba_sessions > 0) { @@ -1846,6 +1848,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, del_timer_sync(&baid_data->session_timer); RCU_INIT_POINTER(mvm->baid_map[baid], NULL); kfree_rcu(baid_data, rcu_head); + IWL_DEBUG_HT(mvm, "BAID %d is free\n", baid); } return 0; From 60dec5233cd8651860e8010c953d116fb0f1ba86 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 21 Jun 2016 14:14:08 +0300 Subject: [PATCH 44/56] iwlwifi: mvm: free RX reorder buffer on restart Restart flow zeroes the rx_ba_sessions counter. Mac80211 asks driver to tear down of the session only afterwards, and as a result driver didn't free the data. Fix it. Signed-off-by: Sara Sharon Fixes: 10b2b2019d81 ("iwlwifi: mvm: add infrastructure for tracking BA session in driver") Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index c0c2ea344ad4..61f16c3f4f10 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1828,11 +1828,12 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, mvm_sta->sta_id, tid, baid); WARN_ON(rcu_access_pointer(mvm->baid_map[baid])); rcu_assign_pointer(mvm->baid_map[baid], baid_data); - } else if (mvm->rx_ba_sessions > 0) { + } else { u8 baid = mvm_sta->tid_to_baid[tid]; - /* check that restart flow didn't zero the counter */ - mvm->rx_ba_sessions--; + if (mvm->rx_ba_sessions > 0) + /* check that restart flow didn't zero the counter */ + mvm->rx_ba_sessions--; if (!iwl_mvm_has_new_rx_api(mvm)) return 0; From 24ddddf3673b43708c9e7edc576b4d14a81a43e9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 21 Jun 2016 12:34:36 +0200 Subject: [PATCH 45/56] iwlwifi: store cipher scheme independent of mac80211 In order to reduce reliance on mac80211 structs in the core iwlwifi code, store the cipher schemes in the format given by the firmware and convert it later, rather than storing it in the mac80211 format. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 16 ++-------------- drivers/net/wireless/intel/iwlwifi/iwl-fw.h | 3 +-- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 19 ++++++++++++++++--- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 + 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index e2df544cc64e..d675bf0a9e99 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -326,8 +326,6 @@ static int iwl_store_cscheme(struct iwl_fw *fw, const u8 *data, const u32 len) int i, j; struct iwl_fw_cscheme_list *l = (struct iwl_fw_cscheme_list *)data; struct iwl_fw_cipher_scheme *fwcs; - struct ieee80211_cipher_scheme *cs; - u32 cipher; if (len < sizeof(*l) || len < sizeof(l->size) + l->size * sizeof(l->cs[0])) @@ -335,22 +333,12 @@ static int iwl_store_cscheme(struct iwl_fw *fw, const u8 *data, const u32 len) for (i = 0, j = 0; i < IWL_UCODE_MAX_CS && i < l->size; i++) { fwcs = &l->cs[j]; - cipher = le32_to_cpu(fwcs->cipher); /* we skip schemes with zero cipher suite selector */ - if (!cipher) + if (!fwcs->cipher) continue; - cs = &fw->cs[j++]; - cs->cipher = cipher; - cs->iftype = BIT(NL80211_IFTYPE_STATION); - cs->hdr_len = fwcs->hdr_len; - cs->pn_len = fwcs->pn_len; - cs->pn_off = fwcs->pn_off; - cs->key_idx_off = fwcs->key_idx_off; - cs->key_idx_mask = fwcs->key_idx_mask; - cs->key_idx_shift = fwcs->key_idx_shift; - cs->mic_len = fwcs->mic_len; + fw->cs[j++] = *fwcs; } return 0; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h index 655ec52a0c03..74ea68d1063c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h @@ -67,7 +67,6 @@ #ifndef __iwl_fw_h__ #define __iwl_fw_h__ #include -#include #include "iwl-fw-file.h" #include "iwl-fw-error-dump.h" @@ -287,7 +286,7 @@ struct iwl_fw { enum iwl_fw_type type; - struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS]; + struct iwl_fw_cipher_scheme cs[IWL_UCODE_MAX_CS]; u8 human_readable[FW_VER_HUMAN_READABLE_SZ]; u32 sdio_adma_addr; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index e44c337e604e..73e49d3a8814 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -494,10 +494,23 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) /* currently FW API supports only one optional cipher scheme */ if (mvm->fw->cs[0].cipher) { + const struct iwl_fw_cipher_scheme *fwcs = &mvm->fw->cs[0]; + struct ieee80211_cipher_scheme *cs = &mvm->cs[0]; + mvm->hw->n_cipher_schemes = 1; - mvm->hw->cipher_schemes = &mvm->fw->cs[0]; - mvm->ciphers[hw->wiphy->n_cipher_suites] = - mvm->fw->cs[0].cipher; + + cs->cipher = le32_to_cpu(fwcs->cipher); + cs->iftype = BIT(NL80211_IFTYPE_STATION); + cs->hdr_len = fwcs->hdr_len; + cs->pn_len = fwcs->pn_len; + cs->pn_off = fwcs->pn_off; + cs->key_idx_off = fwcs->key_idx_off; + cs->key_idx_mask = fwcs->key_idx_mask; + cs->key_idx_shift = fwcs->key_idx_shift; + cs->mic_len = fwcs->mic_len; + + mvm->hw->cipher_schemes = mvm->cs; + mvm->ciphers[hw->wiphy->n_cipher_suites] = cs->cipher; hw->wiphy->n_cipher_suites++; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index c440b10b3cde..b4fc86d5d7ef 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1012,6 +1012,7 @@ struct iwl_mvm { struct iwl_mvm_shared_mem_cfg shared_mem_cfg; u32 ciphers[IWL_MVM_NUM_CIPHERS]; + struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS]; struct iwl_mvm_tof_data tof_data; struct ieee80211_vif *nan_vif; From 0c4cb7314d15d63fbc96e60c57443f3ef83b9c5e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 21 Jun 2016 12:44:21 +0200 Subject: [PATCH 46/56] iwlwifi: tracing: decouple from mac80211 In order to be able to properly record SKBs that didn't come through mac80211, don't rely on the IEEE80211_TX_CTRL_PORT_CTRL_PROTO flag but instead check for ETH_P_PAE directly. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/iwl-devtrace.h | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h index f4d3cd010087..545d14b0bc92 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h @@ -1,6 +1,7 @@ /****************************************************************************** * * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. + * 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 @@ -33,11 +34,29 @@ static inline bool iwl_trace_data(struct sk_buff *skb) { struct ieee80211_hdr *hdr = (void *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + __le16 fc = hdr->frame_control; + int offs = 24; /* start with normal header length */ - if (!ieee80211_is_data(hdr->frame_control)) + if (!ieee80211_is_data(fc)) return false; - return !(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO); + + /* Try to determine if the frame is EAPOL. This might have false + * positives (if there's no RFC 1042 header and we compare to some + * payload instead) but since we're only doing tracing that's not + * a problem. + */ + + if (ieee80211_has_a4(fc)) + offs += 6; + if (ieee80211_is_data_qos(fc)) + offs += 2; + /* don't account for crypto - these are unencrypted */ + + /* also account for the RFC 1042 header, of course */ + offs += 6; + + return skb->len > offs + 2 && + *(__be16 *)(skb->data + offs) == cpu_to_be16(ETH_P_PAE); } static inline size_t iwl_rx_trace_len(const struct iwl_trans *trans, From 21cb3222fe569bd09a42771ffc8da9fb4666bd8a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 21 Jun 2016 13:11:48 +0200 Subject: [PATCH 47/56] iwlwifi: decouple PCIe transport from mac80211 The PCIe transport needs to store two pointers in each TX SKB, and currently assumes mac80211's ieee80211_tx_info is present in the CB to do that. In order to remove that assumption, have the opmodes pass in the offset to where the pointers can be stored in the CB and use the offset in the PCIe code. To make the disentanglement complete, remove mac80211.h includes from everywhere in the generic iwlwifi code. This required adding an include of cfg80211.h in one place. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/dvm/main.c | 2 + .../wireless/intel/iwlwifi/iwl-eeprom-parse.h | 1 + .../wireless/intel/iwlwifi/iwl-modparams.h | 1 - .../net/wireless/intel/iwlwifi/iwl-trans.h | 9 ++-- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 3 ++ .../wireless/intel/iwlwifi/pcie/internal.h | 2 + .../net/wireless/intel/iwlwifi/pcie/trans.c | 3 ++ drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 41 ++++++++++--------- 8 files changed, 37 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c index 591591418316..b49848683587 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c @@ -1337,6 +1337,8 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, trans_cfg.command_groups_size = ARRAY_SIZE(iwl_dvm_groups); trans_cfg.cmd_fifo = IWLAGN_CMD_FIFO_NUM; + trans_cfg.cb_data_offs = offsetof(struct ieee80211_tx_info, + driver_data[2]); WARN_ON(sizeof(priv->transport_queue_stop) * BITS_PER_BYTE < priv->cfg->base_params->num_of_queues); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h index 1f4e50289c14..e04a91d70a15 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h @@ -66,6 +66,7 @@ #include #include +#include #include "iwl-trans.h" struct iwl_nvm_data { diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h index 0379899dc847..4d32b10fe50c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h @@ -66,7 +66,6 @@ #include #include #include -#include extern struct iwl_mod_params iwlwifi_mod_params; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 57cc67f092ea..9ac47e02ae69 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -392,11 +392,6 @@ static inline void iwl_free_rxb(struct iwl_rx_cmd_buffer *r) #define MAX_NO_RECLAIM_CMDS 6 -/* - * The first entry in driver_data array in ieee80211_tx_info - * that can be used by the transport. - */ -#define IWL_TRANS_FIRST_DRIVER_DATA 2 #define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo)))) /* @@ -500,6 +495,8 @@ struct iwl_hcmd_arr { * @command_groups_size: number of command groups, to avoid illegal access * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until * we get the ALIVE from the uCode + * @cb_data_offs: offset inside skb->cb to store transport data at, must have + * space for at least two pointers */ struct iwl_trans_config { struct iwl_op_mode *op_mode; @@ -519,6 +516,8 @@ struct iwl_trans_config { int command_groups_size; u32 sdio_adma_addr; + + u8 cb_data_offs; }; struct iwl_trans_dump_data { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index a08db009d32a..55d9096da68c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -668,6 +668,9 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD; trans_cfg.scd_set_active = true; + trans_cfg.cb_data_offs = offsetof(struct ieee80211_tx_info, + driver_data[2]); + trans_cfg.sdio_adma_addr = fw->sdio_adma_addr; trans_cfg.sw_csum_tx = IWL_MVM_SW_TX_CSUM_OFFLOAD; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 8bfa9159d27d..f684b9d21912 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -385,6 +385,8 @@ struct iwl_trans_pcie { wait_queue_head_t wait_command_queue; wait_queue_head_t d0i3_waitq; + u8 page_offs, dev_cmd_offs; + u8 cmd_queue; u8 cmd_fifo; unsigned int cmd_q_wdg_timeout; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index f5ace924e76a..3b7a4146693d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1650,6 +1650,9 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, trans_pcie->scd_set_active = trans_cfg->scd_set_active; trans_pcie->sw_csum_tx = trans_cfg->sw_csum_tx; + trans_pcie->page_offs = trans_cfg->cb_data_offs; + trans_pcie->dev_cmd_offs = trans_cfg->cb_data_offs + sizeof(void *); + trans->command_groups = trans_cfg->command_groups; trans->command_groups_size = trans_cfg->command_groups_size; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 9b5858bc8ee2..2b688260a6a9 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -584,16 +584,16 @@ static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, return 0; } -static void iwl_pcie_free_tso_page(struct sk_buff *skb) +static void iwl_pcie_free_tso_page(struct iwl_trans_pcie *trans_pcie, + struct sk_buff *skb) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct page **page_ptr; - if (info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA]) { - struct page *page = - info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA]; + page_ptr = (void *)((u8 *)skb->cb + trans_pcie->page_offs); - __free_page(page); - info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA] = NULL; + if (*page_ptr) { + __free_page(*page_ptr); + *page_ptr = NULL; } } @@ -639,7 +639,7 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) if (WARN_ON_ONCE(!skb)) continue; - iwl_pcie_free_tso_page(skb); + iwl_pcie_free_tso_page(trans_pcie, skb); } iwl_pcie_txq_free_tfd(trans, txq); q->read_ptr = iwl_queue_inc_wrap(q->read_ptr); @@ -1084,7 +1084,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, if (WARN_ON_ONCE(!skb)) continue; - iwl_pcie_free_tso_page(skb); + iwl_pcie_free_tso_page(trans_pcie, skb); __skb_queue_tail(skbs, skb); @@ -1115,17 +1115,17 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, while (!skb_queue_empty(&overflow_skbs)) { struct sk_buff *skb = __skb_dequeue(&overflow_skbs); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - u8 dev_cmd_idx = IWL_TRANS_FIRST_DRIVER_DATA + 1; - struct iwl_device_cmd *dev_cmd = - info->driver_data[dev_cmd_idx]; + struct iwl_device_cmd *dev_cmd_ptr; + + dev_cmd_ptr = *(void **)((u8 *)skb->cb + + trans_pcie->dev_cmd_offs); /* * Note that we can very well be overflowing again. * 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, txq_id); + iwl_trans_pcie_tx(trans, skb, dev_cmd_ptr, txq_id); } spin_lock_bh(&txq->lock); @@ -2024,7 +2024,6 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_cmd_meta *out_meta, struct iwl_device_cmd *dev_cmd, u16 tb1_len) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_trans_pcie *trans_pcie = txq->trans_pcie; struct ieee80211_hdr *hdr = (void *)skb->data; unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room; @@ -2033,6 +2032,7 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, u16 length, iv_len, amsdu_pad; u8 *start_hdr; struct iwl_tso_hdr_page *hdr_page; + struct page **page_ptr; int ret; struct tso_t tso; @@ -2063,7 +2063,8 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, get_page(hdr_page->page); start_hdr = hdr_page->pos; - info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA] = hdr_page->page; + page_ptr = (void *)((u8 *)skb->cb + trans_pcie->page_offs); + *page_ptr = hdr_page->page; memcpy(hdr_page->pos, skb->data + hdr_len, iv_len); hdr_page->pos += iv_len; @@ -2273,10 +2274,12 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, /* don't put the packet on the ring, if there is no room */ if (unlikely(iwl_queue_space(q) < 3)) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_device_cmd **dev_cmd_ptr; - info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA + 1] = - dev_cmd; + 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); From f16c3ebfa64fdf0e2dc88e6baa72da95ab70ffd7 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 13 Jun 2016 08:28:26 +0300 Subject: [PATCH 48/56] iwlwifi: pcie: fix a race in firmware loading flow Upon firmware load interrupt (FH_TX), the ISR re-enables the firmware load interrupt only to avoid races with other flows as described in the commit below. When the firmware is completely loaded, the thread that is loading the firmware will enable all the interrupts to make sure that the driver gets the ALIVE interrupt. The problem with that is that the thread that is loading the firmware is actually racing against the ISR and we can get to the following situation: CPU0 CPU1 iwl_pcie_load_given_ucode ... iwl_pcie_load_firmware_chunk wait_for_interrupt ISR handles CSR_INT_BIT_FH_TX ISR wakes up the thread on CPU0 /* enable all the interrupts * to get the ALIVE interrupt */ iwl_enable_interrupts ISR re-enables CSR_INT_BIT_FH_TX only /* start the firmware */ iwl_write32(trans, CSR_RESET, 0); BUG! ALIVE interrupt will never arrive since it has been masked by CPU1. In order to fix that, change the ISR to first check if STATUS_INT_ENABLED is set. If so, re-enable all the interrupts. If STATUS_INT_ENABLED is clear, then we can check what specific interrupt happened and re-enable only that specific interrupt (RFKILL or FH_TX). All the credit for the analysis goes to Kirtika who did the actual debugging work. Cc: [4.5+] Fixes: a6bd005fe92 ("iwlwifi: pcie: fix RF-Kill vs. firmware load race") Signed-off-by: Luca Coelho --- .../wireless/intel/iwlwifi/pcie/internal.h | 21 +++++++++++++++++-- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 18 +++++++++------- .../net/wireless/intel/iwlwifi/pcie/trans.c | 8 ------- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index f684b9d21912..54af3daddd24 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -500,7 +500,7 @@ void iwl_pcie_dump_csr(struct iwl_trans *trans); /***************************************************** * Helpers ******************************************************/ -static inline void iwl_disable_interrupts(struct iwl_trans *trans) +static inline void _iwl_disable_interrupts(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -523,7 +523,16 @@ static inline void iwl_disable_interrupts(struct iwl_trans *trans) IWL_DEBUG_ISR(trans, "Disabled interrupts\n"); } -static inline void iwl_enable_interrupts(struct iwl_trans *trans) +static inline void iwl_disable_interrupts(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + spin_lock(&trans_pcie->irq_lock); + _iwl_disable_interrupts(trans); + spin_unlock(&trans_pcie->irq_lock); +} + +static inline void _iwl_enable_interrupts(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -546,6 +555,14 @@ static inline void iwl_enable_interrupts(struct iwl_trans *trans) } } +static inline void iwl_enable_interrupts(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + spin_lock(&trans_pcie->irq_lock); + _iwl_enable_interrupts(trans); + spin_unlock(&trans_pcie->irq_lock); +} static inline void iwl_enable_hw_int_msk_msix(struct iwl_trans *trans, u32 msk) { 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 0296c290f3a1..45f1b7e716a5 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -1535,7 +1535,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) * have anything to service */ if (test_bit(STATUS_INT_ENABLED, &trans->status)) - iwl_enable_interrupts(trans); + _iwl_enable_interrupts(trans); spin_unlock(&trans_pcie->irq_lock); lock_map_release(&trans->sync_cmd_lockdep_map); return IRQ_NONE; @@ -1727,15 +1727,17 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) inta & ~trans_pcie->inta_mask); } - /* we are loading the firmware, enable FH_TX interrupt only */ - if (handled & CSR_INT_BIT_FH_TX) - iwl_enable_fw_load_int(trans); + spin_lock(&trans_pcie->irq_lock); /* only Re-enable all interrupt if disabled by irq */ - else if (test_bit(STATUS_INT_ENABLED, &trans->status)) - iwl_enable_interrupts(trans); + if (test_bit(STATUS_INT_ENABLED, &trans->status)) + _iwl_enable_interrupts(trans); + /* we are loading the firmware, enable FH_TX interrupt only */ + else if (handled & CSR_INT_BIT_FH_TX) + iwl_enable_fw_load_int(trans); /* Re-enable RF_KILL if it occurred */ else if (handled & CSR_INT_BIT_RF_KILL) iwl_enable_rfkill_int(trans); + spin_unlock(&trans_pcie->irq_lock); out: lock_map_release(&trans->sync_cmd_lockdep_map); @@ -1799,7 +1801,7 @@ void iwl_pcie_reset_ict(struct iwl_trans *trans) return; spin_lock(&trans_pcie->irq_lock); - iwl_disable_interrupts(trans); + _iwl_disable_interrupts(trans); memset(trans_pcie->ict_tbl, 0, ICT_SIZE); @@ -1815,7 +1817,7 @@ void iwl_pcie_reset_ict(struct iwl_trans *trans) trans_pcie->use_ict = true; trans_pcie->ict_index = 0; iwl_write32(trans, CSR_INT, trans_pcie->inta_mask); - iwl_enable_interrupts(trans); + _iwl_enable_interrupts(trans); spin_unlock(&trans_pcie->irq_lock); } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 3b7a4146693d..9e953a4ea657 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1037,9 +1037,7 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) was_hw_rfkill = iwl_is_rfkill_set(trans); /* tell the device to stop sending interrupts */ - spin_lock(&trans_pcie->irq_lock); iwl_disable_interrupts(trans); - spin_unlock(&trans_pcie->irq_lock); /* device going down, Stop using ICT table */ iwl_pcie_disable_ict(trans); @@ -1083,9 +1081,7 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) * the time, unless the interrupt is ACKed even if the interrupt * should be masked. Re-ACK all the interrupts here. */ - spin_lock(&trans_pcie->irq_lock); iwl_disable_interrupts(trans); - spin_unlock(&trans_pcie->irq_lock); /* clear all status bits */ clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); @@ -1578,15 +1574,11 @@ static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans) mutex_lock(&trans_pcie->mutex); /* disable interrupts - don't enable HW RF kill interrupt */ - spin_lock(&trans_pcie->irq_lock); iwl_disable_interrupts(trans); - spin_unlock(&trans_pcie->irq_lock); iwl_pcie_apm_stop(trans, true); - spin_lock(&trans_pcie->irq_lock); iwl_disable_interrupts(trans); - spin_unlock(&trans_pcie->irq_lock); iwl_pcie_disable_ict(trans); From b1753c62c72fe70035498b28901f9a4bdcf2b92a Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 21 Jun 2016 12:44:01 +0300 Subject: [PATCH 49/56] iwlwifi: pcie: track rxb status In MQ environment and new architecture in early stages we may encounter DMA issues. Track RXB status and bail out in case we receive index to an RXB that was not mapped and handed over to HW. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 2 ++ drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 54af3daddd24..190f611af8ee 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -68,12 +68,14 @@ struct iwl_host_cmd; * struct iwl_rx_mem_buffer * @page_dma: bus address of rxb page * @page: driver's pointer to the rxb page + * @invalid: rxb is in driver ownership - not owned by HW * @vid: index of this rxb in the global table */ struct iwl_rx_mem_buffer { dma_addr_t page_dma; struct page *page; u16 vid; + bool invalid; struct list_head list; }; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 45f1b7e716a5..153b3084de0b 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -266,7 +266,7 @@ static void iwl_pcie_rxmq_restock(struct iwl_trans *trans, rxb = list_first_entry(&rxq->rx_free, struct iwl_rx_mem_buffer, list); list_del(&rxb->list); - + rxb->invalid = false; /* 12 first bits are expected to be empty */ WARN_ON(rxb->page_dma & DMA_BIT_MASK(12)); /* Point to Rx buffer via next RBD in circular buffer */ @@ -317,6 +317,7 @@ static void iwl_pcie_rxsq_restock(struct iwl_trans *trans, rxb = list_first_entry(&rxq->rx_free, struct iwl_rx_mem_buffer, list); list_del(&rxb->list); + rxb->invalid = false; /* Point to Rx buffer via next RBD in circular buffer */ bd[rxq->write] = iwl_pcie_dma_addr2rbd_ptr(rxb->page_dma); @@ -961,6 +962,7 @@ int iwl_pcie_rx_init(struct iwl_trans *trans) list_add(&rxb->list, &def_rxq->rx_used); trans_pcie->global_table[i] = rxb; rxb->vid = (u16)(i + 1); + rxb->invalid = true; } iwl_pcie_rxq_alloc_rbs(trans, GFP_KERNEL, def_rxq); @@ -1256,6 +1258,12 @@ static void iwl_pcie_rx_handle(struct iwl_trans *trans, int queue) goto out; } rxb = trans_pcie->global_table[vid - 1]; + if (WARN(rxb->invalid, + "Invalid rxb from HW %u\n", (u32)vid)) { + iwl_force_nmi(trans); + goto out; + } + rxb->invalid = true; } else { rxb = rxq->queue[i]; rxq->queue[i] = NULL; From 8de437c71eb924cc86e4265ab575289b85de2bf5 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 9 Jun 2016 17:56:38 +0300 Subject: [PATCH 50/56] iwlwifi: pcie: generalize and increase the size of scratchbuf Currently the scratch buffer is set to 16 bytes and indicates the size of the bi-directional DMA. However, next HW generation will perform additional offloading, and will write the result in the key location of the TX command, so the size of the bi-directional consistent memory should grow accordingly - increase it to 40. Generalize the code to get rid of now irrelevant scratch references. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- .../wireless/intel/iwlwifi/pcie/internal.h | 37 +++++---- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 75 +++++++++---------- 2 files changed, 54 insertions(+), 58 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 190f611af8ee..c3d89744c35e 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -232,15 +232,16 @@ struct iwl_queue { #define TFD_CMD_SLOTS 32 /* - * The FH will write back to the first TB only, so we need - * to copy some data into the buffer regardless of whether - * it should be mapped or not. This indicates how big the - * first TB must be to include the scratch buffer. Since - * the scratch is 4 bytes at offset 12, it's 16 now. If we - * make it bigger then allocations will be bigger and copy - * slower, so that's probably not useful. + * The FH will write back to the first TB only, so we need to copy some data + * into the buffer regardless of whether it should be mapped or not. + * This indicates how big the first TB must be to include the scratch buffer + * and the assigned PN. + * Since PN location is 16 bytes at offset 24, it's 40 now. + * If we make it bigger then allocations will be bigger and copy slower, so + * that's probably not useful. */ -#define IWL_HCMD_SCRATCHBUF_SIZE 16 +#define IWL_FIRST_TB_SIZE 40 +#define IWL_FIRST_TB_SIZE_ALIGN ALIGN(IWL_FIRST_TB_SIZE, 64) struct iwl_pcie_txq_entry { struct iwl_device_cmd *cmd; @@ -250,20 +251,18 @@ struct iwl_pcie_txq_entry { struct iwl_cmd_meta meta; }; -struct iwl_pcie_txq_scratch_buf { - struct iwl_cmd_header hdr; - u8 buf[8]; - __le32 scratch; +struct iwl_pcie_first_tb_buf { + u8 buf[IWL_FIRST_TB_SIZE_ALIGN]; }; /** * struct iwl_txq - Tx Queue for DMA * @q: generic Rx/Tx queue descriptor * @tfds: transmit frame descriptors (DMA memory) - * @scratchbufs: start of command headers, including scratch buffers, for + * @first_tb_bufs: start of command headers, including scratch buffers, for * the writeback -- this is DMA memory and an array holding one buffer * for each command on the queue - * @scratchbufs_dma: DMA address for the scratchbufs start + * @first_tb_dma: DMA address for the first_tb_bufs start * @entries: transmit entries (driver state) * @lock: queue lock * @stuck_timer: timer that fires if queue gets stuck @@ -281,8 +280,8 @@ struct iwl_pcie_txq_scratch_buf { struct iwl_txq { struct iwl_queue q; struct iwl_tfd *tfds; - struct iwl_pcie_txq_scratch_buf *scratchbufs; - dma_addr_t scratchbufs_dma; + struct iwl_pcie_first_tb_buf *first_tb_bufs; + dma_addr_t first_tb_dma; struct iwl_pcie_txq_entry *entries; spinlock_t lock; unsigned long frozen_expiry_remainder; @@ -298,10 +297,10 @@ struct iwl_txq { }; static inline dma_addr_t -iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx) +iwl_pcie_get_first_tb_dma(struct iwl_txq *txq, int idx) { - return txq->scratchbufs_dma + - sizeof(struct iwl_pcie_txq_scratch_buf) * idx; + return txq->first_tb_dma + + sizeof(struct iwl_pcie_first_tb_buf) * idx; } struct iwl_tso_hdr_page { diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 2b688260a6a9..9a1c00623247 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -393,7 +393,7 @@ static void iwl_pcie_tfd_unmap(struct iwl_trans *trans, return; } - /* first TB is never freed - it's the scratchbuf data */ + /* first TB is never freed - it's the bidirectional DMA data */ for (i = 1; i < num_tbs; i++) { if (meta->flags & BIT(i + CMD_TB_BITMAP_POS)) @@ -491,7 +491,7 @@ static int iwl_pcie_txq_alloc(struct iwl_trans *trans, { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); size_t tfd_sz = sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX; - size_t scratchbuf_sz; + size_t tb0_buf_sz; int i; if (WARN_ON(txq->entries || txq->tfds)) @@ -526,17 +526,14 @@ static int iwl_pcie_txq_alloc(struct iwl_trans *trans, if (!txq->tfds) goto error; - BUILD_BUG_ON(IWL_HCMD_SCRATCHBUF_SIZE != sizeof(*txq->scratchbufs)); - BUILD_BUG_ON(offsetof(struct iwl_pcie_txq_scratch_buf, scratch) != - sizeof(struct iwl_cmd_header) + - offsetof(struct iwl_tx_cmd, scratch)); + BUILD_BUG_ON(IWL_FIRST_TB_SIZE_ALIGN != sizeof(*txq->first_tb_bufs)); - scratchbuf_sz = sizeof(*txq->scratchbufs) * slots_num; + tb0_buf_sz = sizeof(*txq->first_tb_bufs) * slots_num; - txq->scratchbufs = dma_alloc_coherent(trans->dev, scratchbuf_sz, - &txq->scratchbufs_dma, + txq->first_tb_bufs = dma_alloc_coherent(trans->dev, tb0_buf_sz, + &txq->first_tb_dma, GFP_KERNEL); - if (!txq->scratchbufs) + if (!txq->first_tb_bufs) goto err_free_tfds; txq->q.id = txq_id; @@ -708,8 +705,8 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id) txq->tfds = NULL; dma_free_coherent(dev, - sizeof(*txq->scratchbufs) * txq->q.n_window, - txq->scratchbufs, txq->scratchbufs_dma); + sizeof(*txq->first_tb_bufs) * txq->q.n_window, + txq->first_tb_bufs, txq->first_tb_dma); } kfree(txq->entries); @@ -1422,7 +1419,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, void *dup_buf = NULL; dma_addr_t phys_addr; int idx; - u16 copy_size, cmd_size, scratch_size; + u16 copy_size, cmd_size, tb0_size; bool had_nocopy = false; u8 group_id = iwl_cmd_groupid(cmd->id); int i, ret; @@ -1453,9 +1450,9 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, if (!cmd->len[i]) continue; - /* need at least IWL_HCMD_SCRATCHBUF_SIZE copied */ - if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) { - int copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size; + /* need at least IWL_FIRST_TB_SIZE copied */ + if (copy_size < IWL_FIRST_TB_SIZE) { + int copy = IWL_FIRST_TB_SIZE - copy_size; if (copy > cmdlen[i]) copy = cmdlen[i]; @@ -1576,8 +1573,8 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, } /* - * Otherwise we need at least IWL_HCMD_SCRATCHBUF_SIZE copied - * in total (for the scratchbuf handling), but copy up to what + * Otherwise we need at least IWL_FIRST_TB_SIZE copied + * in total (for bi-directional DMA), but copy up to what * we can fit into the payload for debug dump purposes. */ copy = min_t(int, TFD_MAX_PAYLOAD_SIZE - cmd_pos, cmd->len[i]); @@ -1586,8 +1583,8 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, cmd_pos += copy; /* However, treat copy_size the proper way, we need it below */ - if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) { - copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size; + if (copy_size < IWL_FIRST_TB_SIZE) { + copy = IWL_FIRST_TB_SIZE - copy_size; if (copy > cmd->len[i]) copy = cmd->len[i]; @@ -1602,18 +1599,18 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, le16_to_cpu(out_cmd->hdr.sequence), cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue); - /* start the TFD with the scratchbuf */ - scratch_size = min_t(int, copy_size, IWL_HCMD_SCRATCHBUF_SIZE); - memcpy(&txq->scratchbufs[idx], &out_cmd->hdr, scratch_size); + /* start the TFD with the minimum copy bytes */ + tb0_size = min_t(int, copy_size, IWL_FIRST_TB_SIZE); + memcpy(&txq->first_tb_bufs[idx], &out_cmd->hdr, tb0_size); iwl_pcie_txq_build_tfd(trans, txq, - iwl_pcie_get_scratchbuf_dma(txq, idx), - scratch_size, true); + iwl_pcie_get_first_tb_dma(txq, idx), + tb0_size, true); /* map first command fragment, if any remains */ - if (copy_size > scratch_size) { + if (copy_size > tb0_size) { phys_addr = dma_map_single(trans->dev, - ((u8 *)&out_cmd->hdr) + scratch_size, - copy_size - scratch_size, + ((u8 *)&out_cmd->hdr) + tb0_size, + copy_size - tb0_size, DMA_TO_DEVICE); if (dma_mapping_error(trans->dev, phys_addr)) { iwl_pcie_tfd_unmap(trans, out_meta, @@ -1623,7 +1620,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, } iwl_pcie_txq_build_tfd(trans, txq, phys_addr, - copy_size - scratch_size, false); + copy_size - tb0_size, false); } /* map the remaining (adjusted) nocopy/dup fragments */ @@ -1968,7 +1965,7 @@ static int iwl_fill_data_tbs(struct iwl_trans *trans, struct sk_buff *skb, trace_iwlwifi_dev_tx(trans->dev, skb, &txq->tfds[txq->q.write_ptr], sizeof(struct iwl_tfd), - &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len, + &dev_cmd->hdr, IWL_FIRST_TB_SIZE + tb1_len, skb->data + hdr_len, tb2_len); trace_iwlwifi_dev_tx_data(trans->dev, skb, hdr_len, skb->len - hdr_len); @@ -2044,7 +2041,7 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, trace_iwlwifi_dev_tx(trans->dev, skb, &txq->tfds[txq->q.write_ptr], sizeof(struct iwl_tfd), - &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len, + &dev_cmd->hdr, IWL_FIRST_TB_SIZE + tb1_len, NULL, 0); ip_hdrlen = skb_transport_header(skb) - skb_network_header(skb); @@ -2306,7 +2303,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | INDEX_TO_SEQ(q->write_ptr))); - tb0_phys = iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr); + tb0_phys = iwl_pcie_get_first_tb_dma(txq, q->write_ptr); scratch_phys = tb0_phys + sizeof(struct iwl_cmd_header) + offsetof(struct iwl_tx_cmd, scratch); @@ -2324,7 +2321,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, * setup of the first TB) */ len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) + - hdr_len - IWL_HCMD_SCRATCHBUF_SIZE; + hdr_len - IWL_FIRST_TB_SIZE; /* do not align A-MSDU to dword as the subframe header aligns it */ amsdu = ieee80211_is_data_qos(fc) && (*ieee80211_get_qos_ctl(hdr) & @@ -2338,17 +2335,17 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, tb1_len = len; } - /* The first TB points to the scratchbuf data - min_copy bytes */ - memcpy(&txq->scratchbufs[q->write_ptr], &dev_cmd->hdr, - IWL_HCMD_SCRATCHBUF_SIZE); + /* The first TB points to bi-directional DMA data */ + memcpy(&txq->first_tb_bufs[q->write_ptr], &dev_cmd->hdr, + IWL_FIRST_TB_SIZE); iwl_pcie_txq_build_tfd(trans, txq, tb0_phys, - IWL_HCMD_SCRATCHBUF_SIZE, true); + IWL_FIRST_TB_SIZE, true); /* there must be data left over for TB1 or this code must be changed */ - BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_HCMD_SCRATCHBUF_SIZE); + BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_FIRST_TB_SIZE); /* map the data for TB1 */ - tb1_addr = ((u8 *)&dev_cmd->hdr) + IWL_HCMD_SCRATCHBUF_SIZE; + tb1_addr = ((u8 *)&dev_cmd->hdr) + IWL_FIRST_TB_SIZE; tb1_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(trans->dev, tb1_phys))) goto out_err; From 12a17458a2078342de874a0bdc5d7b3e4ac01289 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 23 Jun 2016 12:04:55 +0300 Subject: [PATCH 51/56] iwlwifi: centralize 64 bit HW registers write Move the write_prph_64 of pcie to be transport agnostic. Add direct write as well, as it is needed for a000 HW. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- .../wireless/intel/iwlwifi/iwl-devtrace-io.h | 35 +++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/iwl-io.c | 27 ++++++++++++++ drivers/net/wireless/intel/iwlwifi/iwl-io.h | 3 ++ drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 25 +++++-------- 4 files changed, 74 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h index 27914eedc146..1dccae6532cf 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h @@ -1,6 +1,7 @@ /****************************************************************************** * * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. + * 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 @@ -83,6 +84,23 @@ TRACE_EVENT(iwlwifi_dev_iowrite32, __get_str(dev), __entry->offs, __entry->val) ); +TRACE_EVENT(iwlwifi_dev_iowrite64, + TP_PROTO(const struct device *dev, u64 offs, u64 val), + TP_ARGS(dev, offs, val), + TP_STRUCT__entry( + DEV_ENTRY + __field(u64, offs) + __field(u64, val) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] write io[%llu] = %llu)", + __get_str(dev), __entry->offs, __entry->val) +); + TRACE_EVENT(iwlwifi_dev_iowrite_prph32, TP_PROTO(const struct device *dev, u32 offs, u32 val), TP_ARGS(dev, offs, val), @@ -100,6 +118,23 @@ TRACE_EVENT(iwlwifi_dev_iowrite_prph32, __get_str(dev), __entry->offs, __entry->val) ); +TRACE_EVENT(iwlwifi_dev_iowrite_prph64, + TP_PROTO(const struct device *dev, u64 offs, u64 val), + TP_ARGS(dev, offs, val), + TP_STRUCT__entry( + DEV_ENTRY + __field(u64, offs) + __field(u64, val) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] write PRPH[%llu] = %llu)", + __get_str(dev), __entry->offs, __entry->val) +); + TRACE_EVENT(iwlwifi_dev_ioread_prph32, TP_PROTO(const struct device *dev, u32 offs, u32 val), TP_ARGS(dev, offs, val), diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index d8b430688872..92c8b5f9a9cb 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -51,6 +51,14 @@ void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val) } IWL_EXPORT_SYMBOL(iwl_write32); +void iwl_write64(struct iwl_trans *trans, u64 ofs, u64 val) +{ + trace_iwlwifi_dev_iowrite64(trans->dev, ofs, val); + iwl_trans_write32(trans, ofs, val & 0xffffffff); + iwl_trans_write32(trans, ofs + 4, val >> 32); +} +IWL_EXPORT_SYMBOL(iwl_write64); + u32 iwl_read32(struct iwl_trans *trans, u32 ofs) { u32 val = iwl_trans_read32(trans, ofs); @@ -102,6 +110,17 @@ void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value) } IWL_EXPORT_SYMBOL(iwl_write_direct32); +void iwl_write_direct64(struct iwl_trans *trans, u64 reg, u64 value) +{ + unsigned long flags; + + if (iwl_trans_grab_nic_access(trans, &flags)) { + iwl_write64(trans, reg, value); + iwl_trans_release_nic_access(trans, &flags); + } +} +IWL_EXPORT_SYMBOL(iwl_write_direct64); + int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, int timeout) { @@ -133,6 +152,14 @@ void iwl_write_prph_no_grab(struct iwl_trans *trans, u32 ofs, u32 val) } IWL_EXPORT_SYMBOL(iwl_write_prph_no_grab); +void iwl_write_prph64_no_grab(struct iwl_trans *trans, u64 ofs, u64 val) +{ + trace_iwlwifi_dev_iowrite_prph64(trans->dev, ofs, val); + iwl_write_prph_no_grab(trans, ofs, val & 0xffffffff); + iwl_write_prph_no_grab(trans, ofs + 4, val >> 32); +} +IWL_EXPORT_SYMBOL(iwl_write_prph64_no_grab); + u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs) { unsigned long flags; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-io.h index a9bcc788cae1..5c8c0e130194 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.h @@ -34,6 +34,7 @@ void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val); void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val); +void iwl_write64(struct iwl_trans *trans, u64 ofs, u64 val); u32 iwl_read32(struct iwl_trans *trans, u32 ofs); static inline void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask) @@ -53,11 +54,13 @@ int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg); void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value); +void iwl_write_direct64(struct iwl_trans *trans, u64 reg, u64 value); u32 iwl_read_prph_no_grab(struct iwl_trans *trans, u32 ofs); u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs); void iwl_write_prph_no_grab(struct iwl_trans *trans, u32 ofs, u32 val); +void iwl_write_prph64_no_grab(struct iwl_trans *trans, u64 ofs, u64 val); void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val); int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr, u32 bits, u32 mask, int timeout); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 153b3084de0b..5c36e6d00622 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -161,13 +161,6 @@ static inline __le32 iwl_pcie_dma_addr2rbd_ptr(dma_addr_t dma_addr) return cpu_to_le32((u32)(dma_addr >> 8)); } -static void iwl_pcie_write_prph_64_no_grab(struct iwl_trans *trans, u64 ofs, - u64 val) -{ - iwl_write_prph_no_grab(trans, ofs, val & 0xffffffff); - iwl_write_prph_no_grab(trans, ofs + 4, val >> 32); -} - /* * iwl_pcie_rx_stop - stops the Rx DMA */ @@ -817,17 +810,17 @@ static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans) for (i = 0; i < trans->num_rx_queues; i++) { /* Tell device where to find RBD free table in DRAM */ - iwl_pcie_write_prph_64_no_grab(trans, - RFH_Q_FRBDCB_BA_LSB(i), - trans_pcie->rxq[i].bd_dma); + iwl_write_prph64_no_grab(trans, + RFH_Q_FRBDCB_BA_LSB(i), + trans_pcie->rxq[i].bd_dma); /* Tell device where to find RBD used table in DRAM */ - iwl_pcie_write_prph_64_no_grab(trans, - RFH_Q_URBDCB_BA_LSB(i), - trans_pcie->rxq[i].used_bd_dma); + iwl_write_prph64_no_grab(trans, + RFH_Q_URBDCB_BA_LSB(i), + trans_pcie->rxq[i].used_bd_dma); /* Tell device where in DRAM to update its Rx status */ - iwl_pcie_write_prph_64_no_grab(trans, - RFH_Q_URBD_STTS_WPTR_LSB(i), - trans_pcie->rxq[i].rb_stts_dma); + iwl_write_prph64_no_grab(trans, + RFH_Q_URBD_STTS_WPTR_LSB(i), + trans_pcie->rxq[i].rb_stts_dma); /* Reset device indice tables */ iwl_write_prph_no_grab(trans, RFH_Q_FRBDCB_WIDX(i), 0); iwl_write_prph_no_grab(trans, RFH_Q_FRBDCB_RIDX(i), 0); From e22744af3b79c42478888b20a063384f5349a21d Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 22 Jun 2016 17:23:34 +0300 Subject: [PATCH 52/56] iwlwifi: pcie: initialize a000 device's TFD table For a000 device the FH was replaced by the TFH. This is the first patch in a series introducing the changes stemming from this change. This patch initializes the TFQ queue table with the new 64 bit register and the relevant TFH configuration registers. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-a000.c | 3 +- .../net/wireless/intel/iwlwifi/iwl-config.h | 3 +- drivers/net/wireless/intel/iwlwifi/iwl-fh.h | 40 ++++++++++++++++++- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 27 ++++++++++--- 4 files changed, 65 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c index 220767e02734..4d78232c8afe 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c @@ -115,7 +115,8 @@ static const struct iwl_ht_params iwl_a000_ht_params = { .apmg_not_supported = true, \ .mq_rx_supported = true, \ .vht_mu_mimo_supported = true, \ - .mac_addr_from_csr = true + .mac_addr_from_csr = true, \ + .use_tfh = true const struct iwl_cfg iwla000_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC a000", diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 64108cc975cf..423b23320d4f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -365,7 +365,8 @@ struct iwl_cfg { mq_rx_supported:1, vht_mu_mimo_supported:1, rf_id:1, - integrated:1; + integrated:1, + use_tfh:1; u8 valid_tx_ant; u8 valid_rx_ant; u8 non_shared_ant; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h index 73f0ed88b20b..3b7346797c8a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h @@ -77,6 +77,7 @@ */ #define FH_MEM_LOWER_BOUND (0x1000) #define FH_MEM_UPPER_BOUND (0x2000) +#define TFH_MEM_LOWER_BOUND (0xA06000) /** * Keep-Warm (KW) buffer base address. @@ -118,10 +119,17 @@ #define FH_MEM_CBBC_16_19_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xC00) #define FH_MEM_CBBC_20_31_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xB20) #define FH_MEM_CBBC_20_31_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xB80) +/* a000 TFD table address, 64 bit */ +#define TFH_TFDQ_CBB_TABLE (TFH_MEM_LOWER_BOUND + 0x1C00) /* Find TFD CB base pointer for given queue */ -static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl) +static inline unsigned int FH_MEM_CBBC_QUEUE(struct iwl_trans *trans, + unsigned int chnl) { + if (trans->cfg->use_tfh) { + WARN_ON_ONCE(chnl >= 64); + return TFH_TFDQ_CBB_TABLE + 8 * chnl; + } if (chnl < 16) return FH_MEM_CBBC_0_15_LOWER_BOUND + 4 * chnl; if (chnl < 20) @@ -130,6 +138,36 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl) return FH_MEM_CBBC_20_31_LOWER_BOUND + 4 * (chnl - 20); } +/* a000 configuration registers */ + +/* + * TFH Configuration register. + * + * BIT fields: + * + * Bits 3:0: + * Define the maximum number of pending read requests. + * Maximum configration value allowed is 0xC + * Bits 9:8: + * Define the maximum transfer size. (64 / 128 / 256) + * Bit 10: + * When bit is set and transfer size is set to 128B, the TFH will enable + * reading chunks of more than 64B only if the read address is aligned to 128B. + * In case of DRAM read address which is not aligned to 128B, the TFH will + * enable transfer size which doesn't cross 64B DRAM address boundary. +*/ +#define TFH_TRANSFER_MODE (TFH_MEM_LOWER_BOUND + 0x1F40) +#define TFH_TRANSFER_MAX_PENDING_REQ 0xc +#define TFH_CHUNK_SIZE_128 BIT(8) +#define TFH_CHUNK_SPLIT_MODE BIT(10) +/* + * Defines the offset address in dwords referring from the beginning of the + * Tx CMD which will be updated in DRAM. + * Note that the TFH offset address for Tx CMD update is always referring to + * the start of the TFD first TB. + * In case of a DRAM Tx CMD update the TFH will update PN and Key ID + */ +#define TFH_TXCMD_UPDATE_CFG (TFH_MEM_LOWER_BOUND + 0x1F48) /** * Rx SRAM Control and Status Registers (RSCSR) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 9a1c00623247..d88d64c92e25 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -70,6 +70,7 @@ * Tx queue resumed. * ***************************************************/ + static int iwl_queue_space(const struct iwl_queue *q) { unsigned int max; @@ -575,8 +576,13 @@ static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, * Tell nic where to find circular buffer of Tx Frame Descriptors for * given Tx queue, and enable the DMA channel used for that queue. * Circular buffer (TFD queue in DRAM) physical base address */ - iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id), - txq->q.dma_addr >> 8); + if (trans->cfg->use_tfh) + iwl_write_direct64(trans, + FH_MEM_CBBC_QUEUE(trans, txq_id), + txq->q.dma_addr); + else + iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(trans, txq_id), + txq->q.dma_addr >> 8); return 0; } @@ -783,9 +789,14 @@ void iwl_trans_pcie_tx_reset(struct iwl_trans *trans) for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; txq_id++) { struct iwl_txq *txq = &trans_pcie->txq[txq_id]; - - iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id), - txq->q.dma_addr >> 8); + if (trans->cfg->use_tfh) + iwl_write_direct64(trans, + FH_MEM_CBBC_QUEUE(trans, txq_id), + txq->q.dma_addr); + else + iwl_write_direct32(trans, + FH_MEM_CBBC_QUEUE(trans, txq_id), + txq->q.dma_addr >> 8); iwl_pcie_txq_unmap(trans, txq_id); txq->q.read_ptr = 0; txq->q.write_ptr = 0; @@ -993,6 +1004,12 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) } } + if (trans->cfg->use_tfh) + iwl_write_direct32(trans, TFH_TRANSFER_MODE, + TFH_TRANSFER_MAX_PENDING_REQ | + TFH_CHUNK_SIZE_128 | + TFH_CHUNK_SPLIT_MODE); + iwl_set_bits_prph(trans, SCD_GP_CTRL, SCD_GP_CTRL_AUTO_ACTIVE_MODE); if (trans->cfg->base_params->num_of_queues > 20) iwl_set_bits_prph(trans, SCD_GP_CTRL, From 564cdce735da0ad036051052e5c01760cc70494f Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 22 Jun 2016 19:25:46 +0300 Subject: [PATCH 53/56] iwlwifi: pcie: load FW chunk for a000 devices Update the firmware load flow for TFH hardware. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-fh.h | 29 ++++++++++ .../net/wireless/intel/iwlwifi/pcie/trans.c | 57 +++++++++++++++---- 2 files changed, 75 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h index 3b7346797c8a..1d6f5d21a663 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h @@ -168,6 +168,35 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(struct iwl_trans *trans, * In case of a DRAM Tx CMD update the TFH will update PN and Key ID */ #define TFH_TXCMD_UPDATE_CFG (TFH_MEM_LOWER_BOUND + 0x1F48) +/* + * Controls TX DMA operation + * + * BIT fields: + * + * Bits 31:30: Enable the SRAM DMA channel. + * Turning on bit 31 will kick the SRAM2DRAM DMA. + * Note that the sram2dram may be enabled only after configuring the DRAM and + * SRAM addresses registers and the byte count register. + * Bits 25:24: Defines the interrupt target upon dram2sram transfer done. When + * set to 1 - interrupt is sent to the driver + * Bit 0: Indicates the snoop configuration +*/ +#define TFH_SRV_DMA_CHNL0_CTRL (TFH_MEM_LOWER_BOUND + 0x1F60) +#define TFH_SRV_DMA_SNOOP BIT(0) +#define TFH_SRV_DMA_TO_DRIVER BIT(24) +#define TFH_SRV_DMA_START BIT(31) + +/* Defines the DMA SRAM write start address to transfer a data block */ +#define TFH_SRV_DMA_CHNL0_SRAM_ADDR (TFH_MEM_LOWER_BOUND + 0x1F64) + +/* Defines the 64bits DRAM start address to read the DMA data block from */ +#define TFH_SRV_DMA_CHNL0_DRAM_ADDR (TFH_MEM_LOWER_BOUND + 0x1F68) + +/* + * Defines the number of bytes to transfer from DRAM to SRAM. + * Note that this register may be configured with non-dword aligned size. + */ +#define TFH_SRV_DMA_CHNL0_BC (TFH_MEM_LOWER_BOUND + 0x1F70) /** * Rx SRAM Control and Status Registers (RSCSR) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 9e953a4ea657..af04dadb30ea 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -608,18 +608,10 @@ static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans) /* * ucode */ -static int iwl_pcie_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr, - dma_addr_t phy_addr, u32 byte_cnt) +static void iwl_pcie_load_firmware_chunk_fh(struct iwl_trans *trans, + u32 dst_addr, dma_addr_t phy_addr, + u32 byte_cnt) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; - int ret; - - trans_pcie->ucode_write_complete = false; - - if (!iwl_trans_grab_nic_access(trans, &flags)) - return -EIO; - iwl_write32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); @@ -642,7 +634,50 @@ static int iwl_pcie_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr, FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); +} +static void iwl_pcie_load_firmware_chunk_tfh(struct iwl_trans *trans, + u32 dst_addr, dma_addr_t phy_addr, + u32 byte_cnt) +{ + /* Stop DMA channel */ + iwl_write32(trans, TFH_SRV_DMA_CHNL0_CTRL, 0); + + /* Configure SRAM address */ + iwl_write32(trans, TFH_SRV_DMA_CHNL0_SRAM_ADDR, + dst_addr); + + /* Configure DRAM address - 64 bit */ + iwl_write64(trans, TFH_SRV_DMA_CHNL0_DRAM_ADDR, phy_addr); + + /* Configure byte count to transfer */ + iwl_write32(trans, TFH_SRV_DMA_CHNL0_BC, byte_cnt); + + /* Enable the DRAM2SRAM to start */ + iwl_write32(trans, TFH_SRV_DMA_CHNL0_CTRL, TFH_SRV_DMA_SNOOP | + TFH_SRV_DMA_TO_DRIVER | + TFH_SRV_DMA_START); +} + +static int iwl_pcie_load_firmware_chunk(struct iwl_trans *trans, + u32 dst_addr, dma_addr_t phy_addr, + u32 byte_cnt) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + int ret; + + trans_pcie->ucode_write_complete = false; + + if (!iwl_trans_grab_nic_access(trans, &flags)) + return -EIO; + + if (trans->cfg->use_tfh) + iwl_pcie_load_firmware_chunk_tfh(trans, dst_addr, phy_addr, + byte_cnt); + else + iwl_pcie_load_firmware_chunk_fh(trans, dst_addr, phy_addr, + byte_cnt); iwl_trans_release_nic_access(trans, &flags); ret = wait_event_timeout(trans_pcie->ucode_write_waitq, From 55bfa4b9d47d905b068b255659cc0ff039af2d21 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Wed, 29 Jun 2016 00:38:40 +0300 Subject: [PATCH 54/56] iwlwifi: mvm: support v4 of the TX power command Add support for the v4 version of the TX power command. Just add a new version and do the same sizing tricks that were done when support for v3 was introduced. This patch doesn't support the new functionality introduced, but makes the driver work with the new size of the command. Signed-off-by: Luca Coelho Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/iwl-fw-file.h | 4 ++++ .../wireless/intel/iwlwifi/mvm/fw-api-power.h | 22 +++++++++++++++---- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 11 ++++++---- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 12 +++++----- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index eb18de800c2c..d56b3d8a31f6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -328,6 +328,9 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t; * @IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG: support getting more shared * memory addresses from the firmware. * @IWL_UCODE_TLV_CAPA_LQM_SUPPORT: supports Link Quality Measurement + * @IWL_UCODE_TLV_CAPA_TX_POWER_ACK: reduced TX power API has larger + * command size (command version 4) that supports toggling ACK TX + * power reduction. * * @NUM_IWL_UCODE_TLV_CAPA: number of bits used */ @@ -368,6 +371,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_USNIFFER_UNIFIED = (__force iwl_ucode_tlv_capa_t)77, IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG = (__force iwl_ucode_tlv_capa_t)80, IWL_UCODE_TLV_CAPA_LQM_SUPPORT = (__force iwl_ucode_tlv_capa_t)81, + IWL_UCODE_TLV_CAPA_TX_POWER_ACK = (__force iwl_ucode_tlv_capa_t)84, NUM_IWL_UCODE_TLV_CAPA #ifdef __CHECKER__ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h index 65a7c8a4cacf..404b0de9e2dc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h @@ -7,7 +7,7 @@ * * 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 - 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 @@ -34,7 +34,7 @@ * * 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 - 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -310,7 +310,8 @@ enum iwl_dev_tx_power_cmd_mode { IWL_TX_POWER_MODE_SET_MAC = 0, IWL_TX_POWER_MODE_SET_DEVICE = 1, IWL_TX_POWER_MODE_SET_CHAINS = 2, -}; /* TX_POWER_REDUCED_FLAGS_TYPE_API_E_VER_2 */; + IWL_TX_POWER_MODE_SET_ACK = 3, +}; /* TX_POWER_REDUCED_FLAGS_TYPE_API_E_VER_4 */; /** * struct iwl_dev_tx_power_cmd_v2 - TX power reduction command @@ -338,7 +339,7 @@ struct iwl_dev_tx_power_cmd_v2 { * @v2: version 2 of the command, embedded here for easier software handling * @per_chain_restriction: per chain restrictions */ -struct iwl_dev_tx_power_cmd { +struct iwl_dev_tx_power_cmd_v3 { /* v3 is just an extension of v2 - keep this here */ struct iwl_dev_tx_power_cmd_v2 v2; __le16 per_chain_restriction[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS]; @@ -346,6 +347,19 @@ struct iwl_dev_tx_power_cmd { #define IWL_DEV_MAX_TX_POWER 0x7FFF +/** + * struct iwl_dev_tx_power_cmd - TX power reduction command + * @v3: version 3 of the command, embedded here for easier software handling + * @enable_ack_reduction: enable or disable close range ack TX power + * reduction. + */ +struct iwl_dev_tx_power_cmd { + /* v4 is just an extension of v3 - keep this here */ + struct iwl_dev_tx_power_cmd_v3 v3; + u8 enable_ack_reduction; + u8 reserved[3]; +} __packed; /* TX_REDUCED_POWER_API_S_VER_4 */ + /** * struct iwl_beacon_filter_cmd * REPLY_BEACON_FILTERING_CMD = 0xd2 (command) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 4c16fa79b462..7e0cdbf8bf74 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1027,9 +1027,10 @@ static int iwl_mvm_sar_init(struct iwl_mvm *mvm) { struct iwl_mvm_sar_table sar_table; struct iwl_dev_tx_power_cmd cmd = { - .v2.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS), + .v3.v2.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS), }; int ret, i, j, idx; + int len = sizeof(cmd); /* we can't do anything with the table if the FW doesn't support it */ if (!fw_has_api(&mvm->fw->ucode_capa, @@ -1039,6 +1040,9 @@ static int iwl_mvm_sar_init(struct iwl_mvm *mvm) return 0; } + if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TX_POWER_ACK)) + len = sizeof(cmd.v3); + ret = iwl_mvm_sar_get_table(mvm, &sar_table); if (ret < 0) { IWL_DEBUG_RADIO(mvm, @@ -1060,15 +1064,14 @@ static int iwl_mvm_sar_init(struct iwl_mvm *mvm) IWL_DEBUG_RADIO(mvm, " Chain[%d]:\n", i); for (j = 0; j < IWL_NUM_SUB_BANDS; j++) { idx = (i * IWL_NUM_SUB_BANDS) + j; - cmd.per_chain_restriction[i][j] = + cmd.v3.per_chain_restriction[i][j] = cpu_to_le16(sar_table.values[idx]); IWL_DEBUG_RADIO(mvm, " Band[%d] = %d * .125dBm\n", j, sar_table.values[idx]); } } - ret = iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, - sizeof(cmd), &cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd); if (ret) IWL_ERR(mvm, "failed to set per-chain TX power: %d\n", ret); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 73e49d3a8814..53761c4f7a0f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1250,18 +1250,20 @@ static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, s16 tx_power) { struct iwl_dev_tx_power_cmd cmd = { - .v2.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_MAC), - .v2.mac_context_id = + .v3.v2.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_MAC), + .v3.v2.mac_context_id = cpu_to_le32(iwl_mvm_vif_from_mac80211(vif)->id), - .v2.pwr_restriction = cpu_to_le16(8 * tx_power), + .v3.v2.pwr_restriction = cpu_to_le16(8 * tx_power), }; int len = sizeof(cmd); if (tx_power == IWL_DEFAULT_MAX_TX_POWER) - cmd.v2.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER); + cmd.v3.v2.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER); + if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TX_POWER_ACK)) + len = sizeof(cmd.v3); if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_TX_POWER_CHAIN)) - len = sizeof(cmd.v2); + len = sizeof(cmd.v3.v2); return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd); } From 38398efb74b63e03c41d13429bf7f8afefe1dbe2 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 30 Jun 2016 11:48:30 +0300 Subject: [PATCH 55/56] iwlwifi: pcie: centralize SCD status logging Centralize the logging of SCD status. The motivation is that for a000 devices we will have new SCD HW, but this code was duplicate anyway, so it is a proper cleanup. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- .../wireless/intel/iwlwifi/pcie/internal.h | 2 + .../net/wireless/intel/iwlwifi/pcie/trans.c | 82 ++++++++++--------- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 36 +------- 3 files changed, 47 insertions(+), 73 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index c3d89744c35e..e3047f232cb2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -476,6 +476,8 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue, bool configure_scd); void iwl_trans_pcie_txq_set_shared_mode(struct iwl_trans *trans, u32 txq_id, bool shared_mode); +void iwl_trans_pcie_log_scd_error(struct iwl_trans *trans, + struct iwl_txq *txq); int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int txq_id); void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index af04dadb30ea..74f2f035bd28 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1950,6 +1950,48 @@ static void iwl_trans_pcie_block_txq_ptrs(struct iwl_trans *trans, bool block) #define IWL_FLUSH_WAIT_MS 2000 +void iwl_trans_pcie_log_scd_error(struct iwl_trans *trans, struct iwl_txq *txq) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 scd_sram_addr; + u8 buf[16]; + int cnt; + + IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", + txq->q.read_ptr, txq->q.write_ptr); + + scd_sram_addr = trans_pcie->scd_base_addr + + SCD_TX_STTS_QUEUE_OFFSET(txq->q.id); + iwl_trans_read_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf)); + + iwl_print_hex_error(trans, buf, sizeof(buf)); + + for (cnt = 0; cnt < FH_TCSR_CHNL_NUM; cnt++) + IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", cnt, + iwl_read_direct32(trans, FH_TX_TRB_REG(cnt))); + + for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { + u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(cnt)); + u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7; + bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE)); + u32 tbl_dw = + iwl_trans_read_mem32(trans, trans_pcie->scd_base_addr + + SCD_TRANS_TBL_OFFSET_QUEUE(cnt)); + + if (cnt & 0x1) + tbl_dw = (tbl_dw & 0xFFFF0000) >> 16; + else + tbl_dw = tbl_dw & 0x0000FFFF; + + IWL_ERR(trans, + "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", + cnt, active ? "" : "in", fifo, tbl_dw, + iwl_read_prph(trans, SCD_QUEUE_RDPTR(cnt)) & + (TFD_QUEUE_SIZE_MAX - 1), + iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt))); + } +} + static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -1957,8 +1999,6 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm) struct iwl_queue *q; int cnt; unsigned long now = jiffies; - u32 scd_sram_addr; - u8 buf[16]; int ret = 0; /* waiting for all the tx frames complete might take a while */ @@ -1998,42 +2038,8 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm) IWL_DEBUG_TX_QUEUES(trans, "Queue %d is now empty.\n", cnt); } - if (!ret) - return 0; - - IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", - txq->q.read_ptr, txq->q.write_ptr); - - scd_sram_addr = trans_pcie->scd_base_addr + - SCD_TX_STTS_QUEUE_OFFSET(txq->q.id); - iwl_trans_read_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf)); - - iwl_print_hex_error(trans, buf, sizeof(buf)); - - for (cnt = 0; cnt < FH_TCSR_CHNL_NUM; cnt++) - IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", cnt, - iwl_read_direct32(trans, FH_TX_TRB_REG(cnt))); - - for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { - u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(cnt)); - u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7; - bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE)); - u32 tbl_dw = - iwl_trans_read_mem32(trans, trans_pcie->scd_base_addr + - SCD_TRANS_TBL_OFFSET_QUEUE(cnt)); - - if (cnt & 0x1) - tbl_dw = (tbl_dw & 0xFFFF0000) >> 16; - else - tbl_dw = tbl_dw & 0x0000FFFF; - - IWL_ERR(trans, - "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", - cnt, active ? "" : "in", fifo, tbl_dw, - iwl_read_prph(trans, SCD_QUEUE_RDPTR(cnt)) & - (TFD_QUEUE_SIZE_MAX - 1), - iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt))); - } + if (ret) + iwl_trans_pcie_log_scd_error(trans, txq); return ret; } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index d88d64c92e25..18650dccdb58 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -155,10 +155,6 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data) struct iwl_txq *txq = (void *)data; struct iwl_trans_pcie *trans_pcie = txq->trans_pcie; struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie); - u32 scd_sram_addr = trans_pcie->scd_base_addr + - SCD_TX_STTS_QUEUE_OFFSET(txq->q.id); - u8 buf[16]; - int i; spin_lock(&txq->lock); /* check if triggered erroneously */ @@ -170,38 +166,8 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data) IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->q.id, jiffies_to_msecs(txq->wd_timeout)); - IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", - txq->q.read_ptr, txq->q.write_ptr); - iwl_trans_read_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf)); - - iwl_print_hex_error(trans, buf, sizeof(buf)); - - for (i = 0; i < FH_TCSR_CHNL_NUM; i++) - IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", i, - iwl_read_direct32(trans, FH_TX_TRB_REG(i))); - - for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { - u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(i)); - u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7; - bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE)); - u32 tbl_dw = - iwl_trans_read_mem32(trans, - trans_pcie->scd_base_addr + - SCD_TRANS_TBL_OFFSET_QUEUE(i)); - - if (i & 0x1) - tbl_dw = (tbl_dw & 0xFFFF0000) >> 16; - else - tbl_dw = tbl_dw & 0x0000FFFF; - - IWL_ERR(trans, - "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", - i, active ? "" : "in", fifo, tbl_dw, - iwl_read_prph(trans, SCD_QUEUE_RDPTR(i)) & - (TFD_QUEUE_SIZE_MAX - 1), - iwl_read_prph(trans, SCD_QUEUE_WRPTR(i))); - } + iwl_trans_pcie_log_scd_error(trans, txq); iwl_force_nmi(trans); } From 6f482e37b75d4e56cdb9b56ae7057f3ce67097bc Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 5 Jul 2016 18:50:17 +0300 Subject: [PATCH 56/56] iwlwifi: move iwl_drv to be shared across transports All transports has this structure. By moving it to be shared, we can get rid of casting to the specific transport in probe and remove. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 2 ++ drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 14 +++++--------- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 2 -- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 9ac47e02ae69..5535e2238da3 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -761,6 +761,7 @@ enum iwl_plat_pm_mode { * @ops - pointer to iwl_trans_ops * @op_mode - pointer to the op_mode * @cfg - pointer to the configuration + * @drv - pointer to iwl_drv * @status: a bit-mask of transport status flags * @dev - pointer to struct device * that represents the device * @max_skb_frags: maximum number of fragments an SKB can have when transmitted. @@ -804,6 +805,7 @@ struct iwl_trans { const struct iwl_trans_ops *ops; struct iwl_op_mode *op_mode; const struct iwl_cfg *cfg; + struct iwl_drv *drv; enum iwl_trans_state state; unsigned long status; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index f70c3e2b1473..78cf9a7f3eac 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -610,7 +610,6 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) const struct iwl_cfg *cfg_7265d __maybe_unused = NULL; const struct iwl_cfg *cfg_9260lc __maybe_unused = NULL; struct iwl_trans *iwl_trans; - struct iwl_trans_pcie *trans_pcie; int ret; iwl_trans = iwl_trans_pcie_alloc(pdev, ent, cfg); @@ -648,12 +647,10 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) #endif pci_set_drvdata(pdev, iwl_trans); + iwl_trans->drv = iwl_drv_start(iwl_trans, cfg); - trans_pcie = IWL_TRANS_GET_PCIE_TRANS(iwl_trans); - trans_pcie->drv = iwl_drv_start(iwl_trans, cfg); - - if (IS_ERR(trans_pcie->drv)) { - ret = PTR_ERR(trans_pcie->drv); + if (IS_ERR(iwl_trans->drv)) { + ret = PTR_ERR(iwl_trans->drv); goto out_free_trans; } @@ -692,7 +689,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; out_free_drv: - iwl_drv_stop(trans_pcie->drv); + iwl_drv_stop(iwl_trans->drv); out_free_trans: iwl_trans_pcie_free(iwl_trans); return ret; @@ -701,7 +698,6 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) static void iwl_pci_remove(struct pci_dev *pdev) { struct iwl_trans *trans = pci_get_drvdata(pdev); - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); /* if RTPM was in use, restore it to the state before probe */ if (trans->runtime_pm_mode != IWL_PLAT_PM_MODE_DISABLED) { @@ -712,7 +708,7 @@ static void iwl_pci_remove(struct pci_dev *pdev) pm_runtime_forbid(trans->dev); } - iwl_drv_stop(trans_pcie->drv); + iwl_drv_stop(trans->drv); iwl_trans_pcie_free(trans); } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index e3047f232cb2..11e347dd44c7 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -314,7 +314,6 @@ struct iwl_tso_hdr_page { * @rx_pool: initial pool of iwl_rx_mem_buffer for all the queues * @global_table: table mapping received VID from hw to rxb * @rba: allocator for RX replenishing - * @drv - pointer to iwl_drv * @trans: pointer to the generic transport area * @scd_base_addr: scheduler sram base address in SRAM * @scd_bc_tbls: pointer to the byte count table of the scheduler @@ -352,7 +351,6 @@ struct iwl_trans_pcie { struct iwl_rx_mem_buffer *global_table[RX_POOL_SIZE]; struct iwl_rb_allocator rba; struct iwl_trans *trans; - struct iwl_drv *drv; struct net_device napi_dev;