Commit 0d770a82 authored by John W. Linville's avatar John W. Linville
parents d294d028 017a6416
...@@ -2,10 +2,6 @@ config IWLWIFI ...@@ -2,10 +2,6 @@ config IWLWIFI
tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlwifi) " tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlwifi) "
depends on PCI && MAC80211 && HAS_IOMEM depends on PCI && MAC80211 && HAS_IOMEM
select FW_LOADER select FW_LOADER
select NEW_LEDS
select LEDS_CLASS
select LEDS_TRIGGERS
select MAC80211_LEDS
---help--- ---help---
Select to build the driver supporting the: Select to build the driver supporting the:
...@@ -43,6 +39,14 @@ config IWLWIFI ...@@ -43,6 +39,14 @@ config IWLWIFI
say M here and read <file:Documentation/kbuild/modules.txt>. The say M here and read <file:Documentation/kbuild/modules.txt>. The
module will be called iwlwifi. module will be called iwlwifi.
config IWLWIFI_LEDS
bool
depends on IWLWIFI
depends on LEDS_CLASS
select LEDS_TRIGGERS
select MAC80211_LEDS
default y
config IWLDVM config IWLDVM
tristate "Intel Wireless WiFi DVM Firmware support" tristate "Intel Wireless WiFi DVM Firmware support"
depends on IWLWIFI depends on IWLWIFI
......
...@@ -4,9 +4,10 @@ iwldvm-objs += main.o rs.o mac80211.o ucode.o tx.o ...@@ -4,9 +4,10 @@ iwldvm-objs += main.o rs.o mac80211.o ucode.o tx.o
iwldvm-objs += lib.o calib.o tt.o sta.o rx.o iwldvm-objs += lib.o calib.o tt.o sta.o rx.o
iwldvm-objs += power.o iwldvm-objs += power.o
iwldvm-objs += scan.o led.o iwldvm-objs += scan.o
iwldvm-objs += rxon.o devices.o iwldvm-objs += rxon.o devices.o
iwldvm-$(CONFIG_IWLWIFI_LEDS) += led.o
iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../
...@@ -888,9 +888,11 @@ struct iwl_priv { ...@@ -888,9 +888,11 @@ struct iwl_priv {
struct iwl_event_log event_log; struct iwl_event_log event_log;
#ifdef CONFIG_IWLWIFI_LEDS
struct led_classdev led; struct led_classdev led;
unsigned long blink_on, blink_off; unsigned long blink_on, blink_off;
bool led_registered; bool led_registered;
#endif
/* WoWLAN GTK rekey data */ /* WoWLAN GTK rekey data */
u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN]; u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN];
......
...@@ -36,8 +36,20 @@ struct iwl_priv; ...@@ -36,8 +36,20 @@ struct iwl_priv;
#define IWL_LED_ACTIVITY (0<<1) #define IWL_LED_ACTIVITY (0<<1)
#define IWL_LED_LINK (1<<1) #define IWL_LED_LINK (1<<1)
#ifdef CONFIG_IWLWIFI_LEDS
void iwlagn_led_enable(struct iwl_priv *priv); void iwlagn_led_enable(struct iwl_priv *priv);
void iwl_leds_init(struct iwl_priv *priv); void iwl_leds_init(struct iwl_priv *priv);
void iwl_leds_exit(struct iwl_priv *priv); void iwl_leds_exit(struct iwl_priv *priv);
#else
static inline void iwlagn_led_enable(struct iwl_priv *priv)
{
}
static inline void iwl_leds_init(struct iwl_priv *priv)
{
}
static inline void iwl_leds_exit(struct iwl_priv *priv)
{
}
#endif
#endif /* __iwl_leds_h__ */ #endif /* __iwl_leds_h__ */
...@@ -98,7 +98,7 @@ ...@@ -98,7 +98,7 @@
#define NVM_HW_SECTION_NUM_FAMILY_7000 0 #define NVM_HW_SECTION_NUM_FAMILY_7000 0
static const struct iwl_base_params iwl7000_base_params = { static const struct iwl_base_params iwl7000_base_params = {
.eeprom_size = OTP_LOW_IMAGE_SIZE, .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_7000,
.num_of_queues = IWLAGN_NUM_QUEUES, .num_of_queues = IWLAGN_NUM_QUEUES,
.pll_cfg_val = 0, .pll_cfg_val = 0,
.shadow_ram_support = true, .shadow_ram_support = true,
......
...@@ -85,7 +85,7 @@ ...@@ -85,7 +85,7 @@
#define NVM_HW_SECTION_NUM_FAMILY_8000 10 #define NVM_HW_SECTION_NUM_FAMILY_8000 10
static const struct iwl_base_params iwl8000_base_params = { static const struct iwl_base_params iwl8000_base_params = {
.eeprom_size = OTP_LOW_IMAGE_SIZE, .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000,
.num_of_queues = IWLAGN_NUM_QUEUES, .num_of_queues = IWLAGN_NUM_QUEUES,
.pll_cfg_val = 0, .pll_cfg_val = 0,
.shadow_ram_support = true, .shadow_ram_support = true,
......
...@@ -102,9 +102,7 @@ ...@@ -102,9 +102,7 @@
/* EEPROM */ /* EEPROM */
#define IWLAGN_EEPROM_IMG_SIZE 2048 #define IWLAGN_EEPROM_IMG_SIZE 2048
/* OTP */
/* lower blocks contain EEPROM image and calibration data */
#define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */
/* high blocks contain PAPD data */ /* high blocks contain PAPD data */
#define OTP_HIGH_IMAGE_SIZE_6x00 (6 * 512 * sizeof(u16)) /* 6 KB */ #define OTP_HIGH_IMAGE_SIZE_6x00 (6 * 512 * sizeof(u16)) /* 6 KB */
#define OTP_HIGH_IMAGE_SIZE_1000 (0x200 * sizeof(u16)) /* 1024 bytes */ #define OTP_HIGH_IMAGE_SIZE_1000 (0x200 * sizeof(u16)) /* 1024 bytes */
......
...@@ -193,6 +193,11 @@ struct iwl_ht_params { ...@@ -193,6 +193,11 @@ struct iwl_ht_params {
#define EEPROM_6000_REG_BAND_24_HT40_CHANNELS 0x80 #define EEPROM_6000_REG_BAND_24_HT40_CHANNELS 0x80
#define EEPROM_REGULATORY_BAND_NO_HT40 0 #define EEPROM_REGULATORY_BAND_NO_HT40 0
/* lower blocks contain EEPROM image and calibration data */
#define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */
#define OTP_LOW_IMAGE_SIZE_FAMILY_7000 (4 * 512 * sizeof(u16)) /* 4 KB */
#define OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(u16)) /* 32 KB */
struct iwl_eeprom_params { struct iwl_eeprom_params {
const u8 regulatory_bands[7]; const u8 regulatory_bands[7];
bool enhanced_txpower; bool enhanced_txpower;
...@@ -269,6 +274,7 @@ struct iwl_cfg { ...@@ -269,6 +274,7 @@ struct iwl_cfg {
u8 nvm_hw_section_num; u8 nvm_hw_section_num;
bool lp_xtal_workaround; bool lp_xtal_workaround;
const struct iwl_pwr_tx_backoff *pwr_tx_backoffs; const struct iwl_pwr_tx_backoff *pwr_tx_backoffs;
bool no_power_up_nic_in_init;
}; };
/* /*
......
...@@ -72,11 +72,14 @@ ...@@ -72,11 +72,14 @@
* @IWL_FW_ERROR_DUMP_SRAM: * @IWL_FW_ERROR_DUMP_SRAM:
* @IWL_FW_ERROR_DUMP_REG: * @IWL_FW_ERROR_DUMP_REG:
* @IWL_FW_ERROR_DUMP_RXF: * @IWL_FW_ERROR_DUMP_RXF:
* @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as
* &struct iwl_fw_error_dump_txcmd packets
*/ */
enum iwl_fw_error_dump_type { enum iwl_fw_error_dump_type {
IWL_FW_ERROR_DUMP_SRAM = 0, IWL_FW_ERROR_DUMP_SRAM = 0,
IWL_FW_ERROR_DUMP_REG = 1, IWL_FW_ERROR_DUMP_REG = 1,
IWL_FW_ERROR_DUMP_RXF = 2, IWL_FW_ERROR_DUMP_RXF = 2,
IWL_FW_ERROR_DUMP_TXCMD = 3,
IWL_FW_ERROR_DUMP_MAX, IWL_FW_ERROR_DUMP_MAX,
}; };
...@@ -105,4 +108,27 @@ struct iwl_fw_error_dump_file { ...@@ -105,4 +108,27 @@ struct iwl_fw_error_dump_file {
u8 data[0]; u8 data[0];
} __packed; } __packed;
/**
* struct iwl_fw_error_dump_txcmd - TX command data
* @cmdlen: original length of command
* @caplen: captured length of command (may be less)
* @data: captured command data, @caplen bytes
*/
struct iwl_fw_error_dump_txcmd {
__le32 cmdlen;
__le32 caplen;
u8 data[];
} __packed;
/**
* iwl_mvm_fw_error_next_data - advance fw error dump data pointer
* @data: previous data block
* Returns: next data block
*/
static inline struct iwl_fw_error_dump_data *
iwl_mvm_fw_error_next_data(struct iwl_fw_error_dump_data *data)
{
return (void *)(data->data + le32_to_cpu(data->len));
}
#endif /* __fw_error_dump_h__ */ #endif /* __fw_error_dump_h__ */
...@@ -116,9 +116,11 @@ enum iwl_ucode_tlv_flag { ...@@ -116,9 +116,11 @@ enum iwl_ucode_tlv_flag {
/** /**
* enum iwl_ucode_tlv_api - ucode api * enum iwl_ucode_tlv_api - ucode api
* @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field. * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
* @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA.
*/ */
enum iwl_ucode_tlv_api { enum iwl_ucode_tlv_api {
IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0), IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0),
IWL_UCODE_TLV_API_CSA_FLOW = BIT(4),
}; };
/** /**
......
...@@ -62,6 +62,7 @@ ...@@ -62,6 +62,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/etherdevice.h>
#include "iwl-drv.h" #include "iwl-drv.h"
#include "iwl-modparams.h" #include "iwl-modparams.h"
#include "iwl-nvm-parse.h" #include "iwl-nvm-parse.h"
...@@ -450,13 +451,7 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg, ...@@ -450,13 +451,7 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg,
struct iwl_nvm_data *data, struct iwl_nvm_data *data,
const __le16 *nvm_sec) const __le16 *nvm_sec)
{ {
u8 hw_addr[ETH_ALEN]; const u8 *hw_addr = (const u8 *)(nvm_sec + HW_ADDR);
if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
memcpy(hw_addr, nvm_sec + HW_ADDR, ETH_ALEN);
else
memcpy(hw_addr, nvm_sec + MAC_ADDRESS_OVERRIDE_FAMILY_8000,
ETH_ALEN);
/* The byte order is little endian 16 bit, meaning 214365 */ /* The byte order is little endian 16 bit, meaning 214365 */
data->hw_addr[0] = hw_addr[1]; data->hw_addr[0] = hw_addr[1];
...@@ -467,6 +462,41 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg, ...@@ -467,6 +462,41 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg,
data->hw_addr[5] = hw_addr[4]; data->hw_addr[5] = hw_addr[4];
} }
static void iwl_set_hw_address_family_8000(const struct iwl_cfg *cfg,
struct iwl_nvm_data *data,
const __le16 *mac_override,
const __le16 *nvm_hw)
{
const u8 *hw_addr;
if (mac_override) {
hw_addr = (const u8 *)(mac_override +
MAC_ADDRESS_OVERRIDE_FAMILY_8000);
/* The byte order is little endian 16 bit, meaning 214365 */
data->hw_addr[0] = hw_addr[1];
data->hw_addr[1] = hw_addr[0];
data->hw_addr[2] = hw_addr[3];
data->hw_addr[3] = hw_addr[2];
data->hw_addr[4] = hw_addr[5];
data->hw_addr[5] = hw_addr[4];
if (is_valid_ether_addr(hw_addr))
return;
}
/* take the MAC address from the OTP */
hw_addr = (const u8 *)(nvm_hw + HW_ADDR0_FAMILY_8000);
data->hw_addr[0] = hw_addr[3];
data->hw_addr[1] = hw_addr[2];
data->hw_addr[2] = hw_addr[1];
data->hw_addr[3] = hw_addr[0];
hw_addr = (const u8 *)(nvm_hw + HW_ADDR1_FAMILY_8000);
data->hw_addr[4] = hw_addr[1];
data->hw_addr[5] = hw_addr[0];
}
struct iwl_nvm_data * struct iwl_nvm_data *
iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
const __le16 *nvm_hw, const __le16 *nvm_sw, const __le16 *nvm_hw, const __le16 *nvm_sw,
...@@ -526,7 +556,7 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, ...@@ -526,7 +556,7 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
rx_chains); rx_chains);
} else { } else {
/* MAC address in family 8000 */ /* MAC address in family 8000 */
iwl_set_hw_address(cfg, data, mac_override); iwl_set_hw_address_family_8000(cfg, data, mac_override, nvm_hw);
iwl_init_sbands(dev, cfg, data, regulatory, iwl_init_sbands(dev, cfg, data, regulatory,
sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains, sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains,
......
...@@ -463,6 +463,11 @@ struct iwl_trans; ...@@ -463,6 +463,11 @@ struct iwl_trans;
* @unref: release a reference previously taken with @ref. Note that * @unref: release a reference previously taken with @ref. Note that
* initially the reference count is 1, making an initial @unref * initially the reference count is 1, making an initial @unref
* necessary to allow low power states. * necessary to allow low power states.
* @dump_data: fill a data dump with debug data, maybe containing last
* TX'ed commands and similar. When called with a NULL buffer and
* zero buffer length, provide only the (estimated) required buffer
* length. Return the used buffer length.
* Note that the transport must fill in the proper file headers.
*/ */
struct iwl_trans_ops { struct iwl_trans_ops {
...@@ -511,6 +516,10 @@ struct iwl_trans_ops { ...@@ -511,6 +516,10 @@ struct iwl_trans_ops {
u32 value); u32 value);
void (*ref)(struct iwl_trans *trans); void (*ref)(struct iwl_trans *trans);
void (*unref)(struct iwl_trans *trans); void (*unref)(struct iwl_trans *trans);
#ifdef CONFIG_IWLWIFI_DEBUGFS
u32 (*dump_data)(struct iwl_trans *trans, void *buf, u32 buflen);
#endif
}; };
/** /**
...@@ -664,6 +673,16 @@ static inline void iwl_trans_unref(struct iwl_trans *trans) ...@@ -664,6 +673,16 @@ static inline void iwl_trans_unref(struct iwl_trans *trans)
trans->ops->unref(trans); trans->ops->unref(trans);
} }
#ifdef CONFIG_IWLWIFI_DEBUGFS
static inline u32 iwl_trans_dump_data(struct iwl_trans *trans,
void *buf, u32 buflen)
{
if (!trans->ops->dump_data)
return 0;
return trans->ops->dump_data(trans, buf, buflen);
}
#endif
static inline int iwl_trans_send_cmd(struct iwl_trans *trans, static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
struct iwl_host_cmd *cmd) struct iwl_host_cmd *cmd)
{ {
......
...@@ -3,8 +3,9 @@ iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o ...@@ -3,8 +3,9 @@ iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o
iwlmvm-y += scan.o time-event.o rs.o iwlmvm-y += scan.o time-event.o rs.o
iwlmvm-y += power.o coex.o iwlmvm-y += power.o coex.o
iwlmvm-y += led.o tt.o offloading.o iwlmvm-y += tt.o offloading.o
iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o
iwlmvm-$(CONFIG_PM_SLEEP) += d3.o iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../
...@@ -611,14 +611,14 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) ...@@ -611,14 +611,14 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO); bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
if (IWL_MVM_BT_COEX_CORUNNING) { if (IWL_MVM_BT_COEX_CORUNNING) {
bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_CORUN_LUT_20 | bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 |
BT_VALID_CORUN_LUT_40); BT_VALID_CORUN_LUT_40);
bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING); bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING);
} }
if (IWL_MVM_BT_COEX_MPLUT) { if (IWL_MVM_BT_COEX_MPLUT) {
bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT); bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT);
bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_MULTI_PRIO_LUT); bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
} }
if (mvm->cfg->bt_shared_single_ant) if (mvm->cfg->bt_shared_single_ant)
......
...@@ -67,7 +67,7 @@ ...@@ -67,7 +67,7 @@
#include "iwl-io.h" #include "iwl-io.h"
#include "iwl-prph.h" #include "iwl-prph.h"
#include "debugfs.h" #include "debugfs.h"
#include "fw-error-dump.h" #include "iwl-fw-error-dump.h"
static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf, static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
......
...@@ -187,9 +187,9 @@ enum iwl_scan_type { ...@@ -187,9 +187,9 @@ enum iwl_scan_type {
* this number of packets were received (typically 1) * this number of packets were received (typically 1)
* @passive2active: is auto switching from passive to active during scan allowed * @passive2active: is auto switching from passive to active during scan allowed
* @rxchain_sel_flags: RXON_RX_CHAIN_* * @rxchain_sel_flags: RXON_RX_CHAIN_*
* @max_out_time: in usecs, max out of serving channel time * @max_out_time: in TUs, max out of serving channel time
* @suspend_time: how long to pause scan when returning to service channel: * @suspend_time: how long to pause scan when returning to service channel:
* bits 0-19: beacon interal in usecs (suspend before executing) * bits 0-19: beacon interal in TUs (suspend before executing)
* bits 20-23: reserved * bits 20-23: reserved
* bits 24-31: number of beacons (suspend between channels) * bits 24-31: number of beacons (suspend between channels)
* @rxon_flags: RXON_FLG_* * @rxon_flags: RXON_FLG_*
...@@ -387,8 +387,8 @@ enum scan_framework_client { ...@@ -387,8 +387,8 @@ enum scan_framework_client {
* @quiet_plcp_th: quiet channel num of packets threshold * @quiet_plcp_th: quiet channel num of packets threshold
* @good_CRC_th: passive to active promotion threshold * @good_CRC_th: passive to active promotion threshold
* @rx_chain: RXON rx chain. * @rx_chain: RXON rx chain.
* @max_out_time: max uSec to be out of assoceated channel * @max_out_time: max TUs to be out of assoceated channel
* @suspend_time: pause scan this long when returning to service channel * @suspend_time: pause scan this TUs when returning to service channel
* @flags: RXON flags * @flags: RXON flags
* @filter_flags: RXONfilter * @filter_flags: RXONfilter
* @tx_cmd: tx command for active scan; for 2GHz and for 5GHz. * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz.
......
...@@ -295,7 +295,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) ...@@ -295,7 +295,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
/* Read the NVM only at driver load time, no need to do this twice */ /* Read the NVM only at driver load time, no need to do this twice */
if (read_nvm) { if (read_nvm) {
/* Read nvm */ /* Read nvm */
ret = iwl_nvm_init(mvm); ret = iwl_nvm_init(mvm, true);
if (ret) { if (ret) {
IWL_ERR(mvm, "Failed to read NVM: %d\n", ret); IWL_ERR(mvm, "Failed to read NVM: %d\n", ret);
goto error; goto error;
......
...@@ -1237,11 +1237,23 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, ...@@ -1237,11 +1237,23 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
u32 rate __maybe_unused = u32 rate __maybe_unused =
le32_to_cpu(beacon->beacon_notify_hdr.initial_rate); le32_to_cpu(beacon->beacon_notify_hdr.initial_rate);
lockdep_assert_held(&mvm->mutex);
IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%16llX rate:%d\n", IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%16llX rate:%d\n",
status & TX_STATUS_MSK, status & TX_STATUS_MSK,
beacon->beacon_notify_hdr.failure_frame, beacon->beacon_notify_hdr.failure_frame,
le64_to_cpu(beacon->tsf), le64_to_cpu(beacon->tsf),
rate); rate);
if (unlikely(mvm->csa_vif && mvm->csa_vif->csa_active)) {
if (!ieee80211_csa_is_complete(mvm->csa_vif)) {
iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm->csa_vif);
} else {
ieee80211_csa_finish(mvm->csa_vif);
mvm->csa_vif = NULL;
}
}
return 0; return 0;
} }
......
...@@ -320,6 +320,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) ...@@ -320,6 +320,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD) if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD)
hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_CSA_FLOW)
hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
hw->wiphy->iface_combinations = iwl_mvm_iface_combinations; hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;
hw->wiphy->n_iface_combinations = hw->wiphy->n_iface_combinations =
ARRAY_SIZE(iwl_mvm_iface_combinations); ARRAY_SIZE(iwl_mvm_iface_combinations);
...@@ -539,13 +542,22 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, ...@@ -539,13 +542,22 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
return -EACCES; return -EACCES;
/* return from D0i3 before starting a new Tx aggregation */ /* return from D0i3 before starting a new Tx aggregation */
if (action == IEEE80211_AMPDU_TX_START) { switch (action) {
case IEEE80211_AMPDU_TX_START:
case IEEE80211_AMPDU_TX_STOP_CONT:
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
case IEEE80211_AMPDU_TX_OPERATIONAL:
iwl_mvm_ref(mvm, IWL_MVM_REF_TX_AGG); iwl_mvm_ref(mvm, IWL_MVM_REF_TX_AGG);
tx_agg_ref = true; tx_agg_ref = true;
/* /*
* wait synchronously until D0i3 exit to get the correct * for tx start, wait synchronously until D0i3 exit to
* sequence number for the tid * get the correct sequence number for the tid.
* additionally, some other ampdu actions use direct
* target access, which is not handled automatically
* by the trans layer (unlike commands), so wait for
* d0i3 exit in these cases as well.
*/ */
if (!wait_event_timeout(mvm->d0i3_exit_waitq, if (!wait_event_timeout(mvm->d0i3_exit_waitq,
!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), HZ)) { !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), HZ)) {
...@@ -553,6 +565,9 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, ...@@ -553,6 +565,9 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG); iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG);
return -EIO; return -EIO;
} }
break;
default:
break;
} }
mutex_lock(&mvm->mutex); mutex_lock(&mvm->mutex);
...@@ -2186,6 +2201,11 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, ...@@ -2186,6 +2201,11 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
switch (vif->type) { switch (vif->type) {
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
/* Unless it's a CSA flow we have nothing to do here */
if (vif->csa_active) {
mvmvif->ap_ibss_active = true;
break;
}
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
/* /*
* The AP binding flow is handled as part of the start_ap flow * The AP binding flow is handled as part of the start_ap flow
...@@ -2222,6 +2242,12 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, ...@@ -2222,6 +2242,12 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
goto out_remove_binding; goto out_remove_binding;
} }
/* Handle binding during CSA */
if (vif->type == NL80211_IFTYPE_AP) {
iwl_mvm_update_quotas(mvm, vif);
iwl_mvm_mac_ctxt_changed(mvm, vif);
}
goto out_unlock; goto out_unlock;
out_remove_binding: out_remove_binding:
...@@ -2246,13 +2272,20 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, ...@@ -2246,13 +2272,20 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
switch (vif->type) { switch (vif->type) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
goto out_unlock; goto out_unlock;
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
mvmvif->monitor_active = false; mvmvif->monitor_active = false;
iwl_mvm_update_quotas(mvm, NULL); iwl_mvm_update_quotas(mvm, NULL);
break; break;
case NL80211_IFTYPE_AP:
/* This part is triggered only during CSA */
if (!vif->csa_active || !mvmvif->ap_ibss_active)
goto out_unlock;
mvmvif->ap_ibss_active = false;
iwl_mvm_update_quotas(mvm, NULL);
/*TODO: bt_coex notification here? */
default: default:
break; break;
} }
...@@ -2348,6 +2381,53 @@ static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw, ...@@ -2348,6 +2381,53 @@ static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,
} }
#endif #endif
static void iwl_mvm_channel_switch_beacon(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_chan_def *chandef)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
mutex_lock(&mvm->mutex);
if (WARN(mvm->csa_vif && mvm->csa_vif->csa_active,
"Another CSA is already in progress"))
goto out_unlock;
IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n",
chandef->center_freq1);
mvm->csa_vif = vif;
out_unlock:
mutex_unlock(&mvm->mutex);
}
static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, u32 queues, bool drop)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_vif *mvmvif;
struct iwl_mvm_sta *mvmsta;
if (!vif || vif->type != NL80211_IFTYPE_STATION)
return;
mutex_lock(&mvm->mutex);
mvmvif = iwl_mvm_vif_from_mac80211(vif);
mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id);
if (WARN_ON_ONCE(!mvmsta))
goto done;
if (drop) {
if (iwl_mvm_flush_tx_path(mvm, mvmsta->tfd_queue_msk, true))
IWL_ERR(mvm, "flush request fail\n");
} else {
iwl_trans_wait_tx_queue_empty(mvm->trans,
mvmsta->tfd_queue_msk);
}
done:
mutex_unlock(&mvm->mutex);
}
const struct ieee80211_ops iwl_mvm_hw_ops = { const struct ieee80211_ops iwl_mvm_hw_ops = {
.tx = iwl_mvm_mac_tx, .tx = iwl_mvm_mac_tx,
.ampdu_action = iwl_mvm_mac_ampdu_action, .ampdu_action = iwl_mvm_mac_ampdu_action,
...@@ -2371,6 +2451,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = { ...@@ -2371,6 +2451,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
.sta_rc_update = iwl_mvm_sta_rc_update, .sta_rc_update = iwl_mvm_sta_rc_update,
.conf_tx = iwl_mvm_mac_conf_tx, .conf_tx = iwl_mvm_mac_conf_tx,
.mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
.flush = iwl_mvm_mac_flush,
.sched_scan_start = iwl_mvm_mac_sched_scan_start, .sched_scan_start = iwl_mvm_mac_sched_scan_start,
.sched_scan_stop = iwl_mvm_mac_sched_scan_stop, .sched_scan_stop = iwl_mvm_mac_sched_scan_stop,
.set_key = iwl_mvm_mac_set_key, .set_key = iwl_mvm_mac_set_key,
...@@ -2390,6 +2471,8 @@ const struct ieee80211_ops iwl_mvm_hw_ops = { ...@@ -2390,6 +2471,8 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
.set_tim = iwl_mvm_set_tim, .set_tim = iwl_mvm_set_tim,
.channel_switch_beacon = iwl_mvm_channel_switch_beacon,
CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd) CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
......
...@@ -589,7 +589,9 @@ struct iwl_mvm { ...@@ -589,7 +589,9 @@ struct iwl_mvm {
u32 *fw_error_rxf; u32 *fw_error_rxf;
u32 fw_error_rxf_len; u32 fw_error_rxf_len;
#ifdef CONFIG_IWLWIFI_LEDS
struct led_classdev led; struct led_classdev led;
#endif
struct ieee80211_vif *p2p_device_vif; struct ieee80211_vif *p2p_device_vif;
...@@ -642,6 +644,8 @@ struct iwl_mvm { ...@@ -642,6 +644,8 @@ struct iwl_mvm {
/* Indicate if device power save is allowed */ /* Indicate if device power save is allowed */
bool ps_disabled; bool ps_disabled;
struct ieee80211_vif *csa_vif;
}; };
/* Extract MVM priv from op_mode and _hw */ /* Extract MVM priv from op_mode and _hw */
...@@ -757,7 +761,7 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, ...@@ -757,7 +761,7 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
struct iwl_device_cmd *cmd); struct iwl_device_cmd *cmd);
/* NVM */ /* NVM */
int iwl_nvm_init(struct iwl_mvm *mvm); int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic);
int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm); int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm);
int iwl_mvm_up(struct iwl_mvm *mvm); int iwl_mvm_up(struct iwl_mvm *mvm);
...@@ -896,8 +900,18 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, ...@@ -896,8 +900,18 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd); struct iwl_device_cmd *cmd);
#ifdef CONFIG_IWLWIFI_LEDS
int iwl_mvm_leds_init(struct iwl_mvm *mvm); int iwl_mvm_leds_init(struct iwl_mvm *mvm);
void iwl_mvm_leds_exit(struct iwl_mvm *mvm); void iwl_mvm_leds_exit(struct iwl_mvm *mvm);
#else
static inline int iwl_mvm_leds_init(struct iwl_mvm *mvm)
{
return 0;
}
static inline void iwl_mvm_leds_exit(struct iwl_mvm *mvm)
{
}
#endif
/* D3 (WoWLAN, NetDetect) */ /* D3 (WoWLAN, NetDetect) */
int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
......
...@@ -238,13 +238,20 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) ...@@ -238,13 +238,20 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
return NULL; return NULL;
} }
} else { } else {
/* SW and REGULATORY sections are mandatory */
if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
!mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data ||
!mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) { !mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) {
IWL_ERR(mvm, IWL_ERR(mvm,
"Can't parse empty family 8000 NVM sections\n"); "Can't parse empty family 8000 NVM sections\n");
return NULL; return NULL;
} }
/* MAC_OVERRIDE or at least HW section must exist */
if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data &&
!mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) {
IWL_ERR(mvm,
"Can't parse mac_address, empty sections\n");
return NULL;
}
} }
if (WARN_ON(!mvm->cfg)) if (WARN_ON(!mvm->cfg))
...@@ -427,7 +434,7 @@ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm) ...@@ -427,7 +434,7 @@ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm)
return ret; return ret;
} }
int iwl_nvm_init(struct iwl_mvm *mvm) int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
{ {
int ret, i, section; int ret, i, section;
u8 *nvm_buffer, *temp; u8 *nvm_buffer, *temp;
...@@ -437,13 +444,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm) ...@@ -437,13 +444,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS)) if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
return -EINVAL; return -EINVAL;
/* load external NVM if configured */ /* load NVM values from nic */
if (iwlwifi_mod_params.nvm_file) { if (read_nvm_from_nic) {
/* move to External NVM flow */
ret = iwl_mvm_read_external_nvm(mvm);
if (ret)
return ret;
} else {
/* list of NVM sections we are allowed/need to read */ /* list of NVM sections we are allowed/need to read */
if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
nvm_to_read[0] = mvm->cfg->nvm_hw_section_num; nvm_to_read[0] = mvm->cfg->nvm_hw_section_num;
...@@ -463,7 +465,6 @@ int iwl_nvm_init(struct iwl_mvm *mvm) ...@@ -463,7 +465,6 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
/* Read From FW NVM */ /* Read From FW NVM */
IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n");
/* TODO: find correct NVM max size for a section */
nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
GFP_KERNEL); GFP_KERNEL);
if (!nvm_buffer) if (!nvm_buffer)
...@@ -511,6 +512,15 @@ int iwl_nvm_init(struct iwl_mvm *mvm) ...@@ -511,6 +512,15 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
return ret; return ret;
} }
/* load external NVM if configured */
if (iwlwifi_mod_params.nvm_file) {
/* move to External NVM flow */
ret = iwl_mvm_read_external_nvm(mvm);
if (ret)
return ret;
}
/* parse the relevant nvm sections */
mvm->nvm_data = iwl_parse_nvm_sections(mvm); mvm->nvm_data = iwl_parse_nvm_sections(mvm);
if (!mvm->nvm_data) if (!mvm->nvm_data)
return -ENODATA; return -ENODATA;
......
...@@ -79,8 +79,8 @@ ...@@ -79,8 +79,8 @@
#include "iwl-prph.h" #include "iwl-prph.h"
#include "rs.h" #include "rs.h"
#include "fw-api-scan.h" #include "fw-api-scan.h"
#include "fw-error-dump.h"
#include "time-event.h" #include "time-event.h"
#include "iwl-fw-error-dump.h"
/* /*
* module name, copyright, version, etc. * module name, copyright, version, etc.
...@@ -220,7 +220,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { ...@@ -220,7 +220,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false), RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false),
RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true), RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true),
RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false), RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, true),
RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true), RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true),
RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION, RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION,
iwl_mvm_rx_ant_coupling_notif, true), iwl_mvm_rx_ant_coupling_notif, true),
...@@ -467,12 +467,18 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, ...@@ -467,12 +467,18 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
min_backoff = calc_min_backoff(trans, cfg); min_backoff = calc_min_backoff(trans, cfg);
iwl_mvm_tt_initialize(mvm, min_backoff); iwl_mvm_tt_initialize(mvm, min_backoff);
if (WARN(cfg->no_power_up_nic_in_init && !iwlwifi_mod_params.nvm_file,
"not allowing power-up and not having nvm_file\n"))
goto out_free;
/* /*
* If the NVM exists in an external file, * Even if nvm exists in the nvm_file driver should read agin the nvm
* there is no need to unnecessarily power up the NIC at driver load * from the nic because there might be entries that exist in the OTP
* and not in the file.
* for nics with no_power_up_nic_in_init: rely completley on nvm_file
*/ */
if (iwlwifi_mod_params.nvm_file) { if (cfg->no_power_up_nic_in_init && iwlwifi_mod_params.nvm_file) {
err = iwl_nvm_init(mvm); err = iwl_nvm_init(mvm, false);
if (err) if (err)
goto out_free; goto out_free;
} else { } else {
...@@ -519,7 +525,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, ...@@ -519,7 +525,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
out_free: out_free:
iwl_phy_db_free(mvm->phy_db); iwl_phy_db_free(mvm->phy_db);
kfree(mvm->scan_cmd); kfree(mvm->scan_cmd);
if (!iwlwifi_mod_params.nvm_file) if (!cfg->no_power_up_nic_in_init || !iwlwifi_mod_params.nvm_file)
iwl_trans_op_mode_leave(trans); iwl_trans_op_mode_leave(trans);
ieee80211_free_hw(mvm->hw); ieee80211_free_hw(mvm->hw);
return NULL; return NULL;
...@@ -816,6 +822,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) ...@@ -816,6 +822,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
struct iwl_fw_error_dump_file *dump_file; struct iwl_fw_error_dump_file *dump_file;
struct iwl_fw_error_dump_data *dump_data; struct iwl_fw_error_dump_data *dump_data;
u32 file_len; u32 file_len;
u32 trans_len;
lockdep_assert_held(&mvm->mutex); lockdep_assert_held(&mvm->mutex);
...@@ -827,6 +834,10 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) ...@@ -827,6 +834,10 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
sizeof(*dump_file) + sizeof(*dump_file) +
sizeof(*dump_data) * 2; sizeof(*dump_data) * 2;
trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0);
if (trans_len)
file_len += trans_len;
dump_file = vmalloc(file_len); dump_file = vmalloc(file_len);
if (!dump_file) if (!dump_file)
return; return;
...@@ -840,7 +851,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) ...@@ -840,7 +851,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
dump_data->len = cpu_to_le32(mvm->fw_error_rxf_len); dump_data->len = cpu_to_le32(mvm->fw_error_rxf_len);
memcpy(dump_data->data, mvm->fw_error_rxf, mvm->fw_error_rxf_len); memcpy(dump_data->data, mvm->fw_error_rxf, mvm->fw_error_rxf_len);
dump_data = (void *)((u8 *)dump_data->data + mvm->fw_error_rxf_len); dump_data = iwl_mvm_fw_error_next_data(dump_data);
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM); dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM);
dump_data->len = cpu_to_le32(mvm->fw_error_sram_len); dump_data->len = cpu_to_le32(mvm->fw_error_sram_len);
...@@ -858,6 +869,15 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) ...@@ -858,6 +869,15 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
kfree(mvm->fw_error_sram); kfree(mvm->fw_error_sram);
mvm->fw_error_sram = NULL; mvm->fw_error_sram = NULL;
mvm->fw_error_sram_len = 0; mvm->fw_error_sram_len = 0;
if (trans_len) {
void *buf = iwl_mvm_fw_error_next_data(dump_data);
u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf,
trans_len);
dump_data = (void *)((u8 *)buf + real_trans_len);
dump_file->file_len =
cpu_to_le32(file_len - trans_len + real_trans_len);
}
} }
#endif #endif
......
...@@ -202,18 +202,15 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, ...@@ -202,18 +202,15 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
struct cfg80211_chan_def *chandef, struct cfg80211_chan_def *chandef,
u8 chains_static, u8 chains_dynamic) u8 chains_static, u8 chains_dynamic)
{ {
int ret;
WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
ctxt->ref); ctxt->ref);
lockdep_assert_held(&mvm->mutex); lockdep_assert_held(&mvm->mutex);
ctxt->channel = chandef->chan; ctxt->channel = chandef->chan;
ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
chains_static, chains_dynamic,
FW_CTXT_ACTION_ADD, 0);
return ret; return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
chains_static, chains_dynamic,
FW_CTXT_ACTION_ADD, 0);
} }
/* /*
......
...@@ -211,7 +211,7 @@ static const struct rs_tx_column rs_tx_columns[] = { ...@@ -211,7 +211,7 @@ static const struct rs_tx_column rs_tx_columns[] = {
.next_columns = { .next_columns = {
RS_COLUMN_LEGACY_ANT_B, RS_COLUMN_LEGACY_ANT_B,
RS_COLUMN_SISO_ANT_A, RS_COLUMN_SISO_ANT_A,
RS_COLUMN_SISO_ANT_B, RS_COLUMN_MIMO2,
RS_COLUMN_INVALID, RS_COLUMN_INVALID,
RS_COLUMN_INVALID, RS_COLUMN_INVALID,
RS_COLUMN_INVALID, RS_COLUMN_INVALID,
...@@ -223,8 +223,8 @@ static const struct rs_tx_column rs_tx_columns[] = { ...@@ -223,8 +223,8 @@ static const struct rs_tx_column rs_tx_columns[] = {
.ant = ANT_B, .ant = ANT_B,
.next_columns = { .next_columns = {
RS_COLUMN_LEGACY_ANT_A, RS_COLUMN_LEGACY_ANT_A,
RS_COLUMN_SISO_ANT_A,
RS_COLUMN_SISO_ANT_B, RS_COLUMN_SISO_ANT_B,
RS_COLUMN_MIMO2,
RS_COLUMN_INVALID, RS_COLUMN_INVALID,
RS_COLUMN_INVALID, RS_COLUMN_INVALID,
RS_COLUMN_INVALID, RS_COLUMN_INVALID,
...@@ -238,10 +238,10 @@ static const struct rs_tx_column rs_tx_columns[] = { ...@@ -238,10 +238,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
RS_COLUMN_SISO_ANT_B, RS_COLUMN_SISO_ANT_B,
RS_COLUMN_MIMO2, RS_COLUMN_MIMO2,
RS_COLUMN_SISO_ANT_A_SGI, RS_COLUMN_SISO_ANT_A_SGI,
RS_COLUMN_SISO_ANT_B_SGI,
RS_COLUMN_LEGACY_ANT_A, RS_COLUMN_LEGACY_ANT_A,
RS_COLUMN_LEGACY_ANT_B, RS_COLUMN_LEGACY_ANT_B,
RS_COLUMN_INVALID, RS_COLUMN_INVALID,
RS_COLUMN_INVALID,
}, },
.checks = { .checks = {
rs_siso_allow, rs_siso_allow,
...@@ -254,10 +254,10 @@ static const struct rs_tx_column rs_tx_columns[] = { ...@@ -254,10 +254,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
RS_COLUMN_SISO_ANT_A, RS_COLUMN_SISO_ANT_A,
RS_COLUMN_MIMO2, RS_COLUMN_MIMO2,
RS_COLUMN_SISO_ANT_B_SGI, RS_COLUMN_SISO_ANT_B_SGI,
RS_COLUMN_SISO_ANT_A_SGI,
RS_COLUMN_LEGACY_ANT_A, RS_COLUMN_LEGACY_ANT_A,
RS_COLUMN_LEGACY_ANT_B, RS_COLUMN_LEGACY_ANT_B,
RS_COLUMN_INVALID, RS_COLUMN_INVALID,
RS_COLUMN_INVALID,
}, },
.checks = { .checks = {
rs_siso_allow, rs_siso_allow,
...@@ -271,10 +271,10 @@ static const struct rs_tx_column rs_tx_columns[] = { ...@@ -271,10 +271,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
RS_COLUMN_SISO_ANT_B_SGI, RS_COLUMN_SISO_ANT_B_SGI,
RS_COLUMN_MIMO2_SGI, RS_COLUMN_MIMO2_SGI,
RS_COLUMN_SISO_ANT_A, RS_COLUMN_SISO_ANT_A,
RS_COLUMN_SISO_ANT_B,
RS_COLUMN_MIMO2,
RS_COLUMN_LEGACY_ANT_A, RS_COLUMN_LEGACY_ANT_A,
RS_COLUMN_LEGACY_ANT_B, RS_COLUMN_LEGACY_ANT_B,
RS_COLUMN_INVALID,
RS_COLUMN_INVALID,
}, },
.checks = { .checks = {
rs_siso_allow, rs_siso_allow,
...@@ -289,10 +289,10 @@ static const struct rs_tx_column rs_tx_columns[] = { ...@@ -289,10 +289,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
RS_COLUMN_SISO_ANT_A_SGI, RS_COLUMN_SISO_ANT_A_SGI,
RS_COLUMN_MIMO2_SGI, RS_COLUMN_MIMO2_SGI,
RS_COLUMN_SISO_ANT_B, RS_COLUMN_SISO_ANT_B,
RS_COLUMN_SISO_ANT_A,
RS_COLUMN_MIMO2,
RS_COLUMN_LEGACY_ANT_A, RS_COLUMN_LEGACY_ANT_A,
RS_COLUMN_LEGACY_ANT_B, RS_COLUMN_LEGACY_ANT_B,
RS_COLUMN_INVALID,
RS_COLUMN_INVALID,
}, },
.checks = { .checks = {
rs_siso_allow, rs_siso_allow,
...@@ -304,12 +304,12 @@ static const struct rs_tx_column rs_tx_columns[] = { ...@@ -304,12 +304,12 @@ static const struct rs_tx_column rs_tx_columns[] = {
.ant = ANT_AB, .ant = ANT_AB,
.next_columns = { .next_columns = {
RS_COLUMN_SISO_ANT_A, RS_COLUMN_SISO_ANT_A,
RS_COLUMN_SISO_ANT_B,
RS_COLUMN_SISO_ANT_A_SGI,
RS_COLUMN_SISO_ANT_B_SGI,
RS_COLUMN_MIMO2_SGI, RS_COLUMN_MIMO2_SGI,
RS_COLUMN_LEGACY_ANT_A, RS_COLUMN_LEGACY_ANT_A,
RS_COLUMN_LEGACY_ANT_B, RS_COLUMN_LEGACY_ANT_B,
RS_COLUMN_INVALID,
RS_COLUMN_INVALID,
RS_COLUMN_INVALID,
}, },
.checks = { .checks = {
rs_mimo_allow, rs_mimo_allow,
...@@ -321,12 +321,12 @@ static const struct rs_tx_column rs_tx_columns[] = { ...@@ -321,12 +321,12 @@ static const struct rs_tx_column rs_tx_columns[] = {
.sgi = true, .sgi = true,
.next_columns = { .next_columns = {
RS_COLUMN_SISO_ANT_A_SGI, RS_COLUMN_SISO_ANT_A_SGI,
RS_COLUMN_SISO_ANT_B_SGI,
RS_COLUMN_SISO_ANT_A,
RS_COLUMN_SISO_ANT_B,
RS_COLUMN_MIMO2, RS_COLUMN_MIMO2,
RS_COLUMN_LEGACY_ANT_A, RS_COLUMN_LEGACY_ANT_A,
RS_COLUMN_LEGACY_ANT_B, RS_COLUMN_LEGACY_ANT_B,
RS_COLUMN_INVALID,
RS_COLUMN_INVALID,
RS_COLUMN_INVALID,
}, },
.checks = { .checks = {
rs_mimo_allow, rs_mimo_allow,
...@@ -1031,7 +1031,7 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, ...@@ -1031,7 +1031,7 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
return; return;
} }
#ifdef CPTCFG_MAC80211_DEBUGFS #ifdef CONFIG_MAC80211_DEBUGFS
/* Disable last tx check if we are debugging with fixed rate */ /* Disable last tx check if we are debugging with fixed rate */
if (lq_sta->dbg_fixed_rate) { if (lq_sta->dbg_fixed_rate) {
IWL_DEBUG_RATE(mvm, "Fixed rate. avoid rate scaling\n"); IWL_DEBUG_RATE(mvm, "Fixed rate. avoid rate scaling\n");
...@@ -1335,105 +1335,50 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, ...@@ -1335,105 +1335,50 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw); tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw);
} }
/*
* Find starting rate for new "search" high-throughput mode of modulation.
* Goal is to find lowest expected rate (under perfect conditions) that is
* above the current measured throughput of "active" mode, to give new mode
* a fair chance to prove itself without too many challenges.
*
* This gets called when transitioning to more aggressive modulation
* (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive
* (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need
* to decrease to match "active" throughput. When moving from MIMO to SISO,
* bit rate will typically need to increase, but not if performance was bad.
*/
static s32 rs_get_best_rate(struct iwl_mvm *mvm, static s32 rs_get_best_rate(struct iwl_mvm *mvm,
struct iwl_lq_sta *lq_sta, struct iwl_lq_sta *lq_sta,
struct iwl_scale_tbl_info *tbl, /* "search" */ struct iwl_scale_tbl_info *tbl, /* "search" */
u16 rate_mask, s8 index) unsigned long rate_mask, s8 index)
{ {
/* "active" values */
struct iwl_scale_tbl_info *active_tbl = struct iwl_scale_tbl_info *active_tbl =
&(lq_sta->lq_info[lq_sta->active_tbl]); &(lq_sta->lq_info[lq_sta->active_tbl]);
s32 active_sr = active_tbl->win[index].success_ratio; s32 success_ratio = active_tbl->win[index].success_ratio;
s32 active_tpt = active_tbl->expected_tpt[index]; u16 expected_current_tpt = active_tbl->expected_tpt[index];
/* expected "search" throughput */
const u16 *tpt_tbl = tbl->expected_tpt; const u16 *tpt_tbl = tbl->expected_tpt;
s32 new_rate, high, low, start_hi;
u16 high_low; u16 high_low;
s8 rate = index; u32 target_tpt;
int rate_idx;
new_rate = high = low = start_hi = IWL_RATE_INVALID;
while (1) {
high_low = rs_get_adjacent_rate(mvm, rate, rate_mask,
tbl->rate.type);
low = high_low & 0xff;
high = (high_low >> 8) & 0xff;
/* if (success_ratio > RS_SR_NO_DECREASE) {
* Lower the "search" bit rate, to give new "search" mode target_tpt = 100 * expected_current_tpt;
* approximately the same throughput as "active" if: IWL_DEBUG_RATE(mvm,
* "SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n",
* 1) "Active" mode has been working modestly well (but not success_ratio, target_tpt);
* great), and expected "search" throughput (under perfect } else {
* conditions) at candidate rate is above the actual target_tpt = lq_sta->last_tpt;
* measured "active" throughput (but less than expected IWL_DEBUG_RATE(mvm,
* "active" throughput under perfect conditions). "SR %d not thag good. Find rate exceeding ACTUAL_TPT %d\n",
* OR success_ratio, target_tpt);
* 2) "Active" mode has been working perfectly or very well }
* and expected "search" throughput (under perfect
* conditions) at candidate rate is above expected
* "active" throughput (under perfect conditions).
*/
if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) &&
((active_sr > RS_SR_FORCE_DECREASE) &&
(active_sr <= IWL_RATE_HIGH_TH) &&
(tpt_tbl[rate] <= active_tpt))) ||
((active_sr >= IWL_RATE_SCALE_SWITCH) &&
(tpt_tbl[rate] > active_tpt))) {
/* (2nd or later pass)
* If we've already tried to raise the rate, and are
* now trying to lower it, use the higher rate. */
if (start_hi != IWL_RATE_INVALID) {
new_rate = start_hi;
break;
}
new_rate = rate;
/* Loop again with lower rate */
if (low != IWL_RATE_INVALID)
rate = low;
/* Lower rate not available, use the original */ rate_idx = find_first_bit(&rate_mask, BITS_PER_LONG);
else
break;
/* Else try to raise the "search" rate to match "active" */ while (rate_idx != IWL_RATE_INVALID) {
} else { if (target_tpt < (100 * tpt_tbl[rate_idx]))
/* (2nd or later pass) break;
* If we've already tried to lower the rate, and are
* now trying to raise it, use the lower rate. */
if (new_rate != IWL_RATE_INVALID)
break;
/* Loop again with higher rate */ high_low = rs_get_adjacent_rate(mvm, rate_idx, rate_mask,
else if (high != IWL_RATE_INVALID) { tbl->rate.type);
start_hi = high;
rate = high;
/* Higher rate not available, use the original */ rate_idx = (high_low >> 8) & 0xff;
} else {
new_rate = rate;
break;
}
}
} }
return new_rate; IWL_DEBUG_RATE(mvm, "Best rate found %d target_tp %d expected_new %d\n",
rate_idx, target_tpt,
rate_idx != IWL_RATE_INVALID ?
100 * tpt_tbl[rate_idx] : IWL_INVALID_VALUE);
return rate_idx;
} }
static u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta) static u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta)
...@@ -1608,7 +1553,7 @@ static enum rs_column rs_get_next_column(struct iwl_mvm *mvm, ...@@ -1608,7 +1553,7 @@ static enum rs_column rs_get_next_column(struct iwl_mvm *mvm,
tpt = lq_sta->last_tpt / 100; tpt = lq_sta->last_tpt / 100;
expected_tpt_tbl = rs_get_expected_tpt_table(lq_sta, next_col, expected_tpt_tbl = rs_get_expected_tpt_table(lq_sta, next_col,
tbl->rate.bw); rs_bw_from_sta_bw(sta));
if (WARN_ON_ONCE(!expected_tpt_tbl)) if (WARN_ON_ONCE(!expected_tpt_tbl))
continue; continue;
...@@ -1649,7 +1594,7 @@ static int rs_switch_to_column(struct iwl_mvm *mvm, ...@@ -1649,7 +1594,7 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column]; const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column];
u32 sz = (sizeof(struct iwl_scale_tbl_info) - u32 sz = (sizeof(struct iwl_scale_tbl_info) -
(sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
u16 rate_mask = 0; unsigned long rate_mask = 0;
u32 rate_idx = 0; u32 rate_idx = 0;
memcpy(search_tbl, tbl, sz); memcpy(search_tbl, tbl, sz);
...@@ -1691,7 +1636,7 @@ static int rs_switch_to_column(struct iwl_mvm *mvm, ...@@ -1691,7 +1636,7 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
!(BIT(rate_idx) & rate_mask)) { !(BIT(rate_idx) & rate_mask)) {
IWL_DEBUG_RATE(mvm, IWL_DEBUG_RATE(mvm,
"can not switch with index %d" "can not switch with index %d"
" rate mask %x\n", " rate mask %lx\n",
rate_idx, rate_mask); rate_idx, rate_mask);
goto err; goto err;
...@@ -1805,16 +1750,21 @@ static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index, ...@@ -1805,16 +1750,21 @@ static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index,
*stronger = TPC_INVALID; *stronger = TPC_INVALID;
} }
static bool rs_tpc_allowed(struct iwl_mvm *mvm, struct rs_rate *rate, static bool rs_tpc_allowed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
enum ieee80211_band band) struct rs_rate *rate, enum ieee80211_band band)
{ {
int index = rate->index; int index = rate->index;
bool cam = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM);
bool sta_ps_disabled = (vif->type == NL80211_IFTYPE_STATION &&
!vif->bss_conf.ps);
IWL_DEBUG_RATE(mvm, "cam: %d sta_ps_disabled %d\n",
cam, sta_ps_disabled);
/* /*
* allow tpc only if power management is enabled, or bt coex * allow tpc only if power management is enabled, or bt coex
* activity grade allows it and we are on 2.4Ghz. * activity grade allows it and we are on 2.4Ghz.
*/ */
if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM && if ((cam || sta_ps_disabled) &&
!iwl_mvm_bt_coex_is_tpc_allowed(mvm, band)) !iwl_mvm_bt_coex_is_tpc_allowed(mvm, band))
return false; return false;
...@@ -1931,7 +1881,7 @@ static bool rs_tpc_perform(struct iwl_mvm *mvm, ...@@ -1931,7 +1881,7 @@ static bool rs_tpc_perform(struct iwl_mvm *mvm,
band = chanctx_conf->def.chan->band; band = chanctx_conf->def.chan->band;
rcu_read_unlock(); rcu_read_unlock();
if (!rs_tpc_allowed(mvm, rate, band)) { if (!rs_tpc_allowed(mvm, vif, rate, band)) {
IWL_DEBUG_RATE(mvm, IWL_DEBUG_RATE(mvm,
"tpc is not allowed. remove txp restrictions"); "tpc is not allowed. remove txp restrictions");
lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION; lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION;
...@@ -2235,7 +2185,8 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, ...@@ -2235,7 +2185,8 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
break; break;
case RS_ACTION_STAY: case RS_ACTION_STAY:
/* No change */ /* No change */
update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl); if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN)
update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl);
break; break;
default: default:
break; break;
...@@ -2489,10 +2440,6 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta, ...@@ -2489,10 +2440,6 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
if (i == IWL_RATE_9M_INDEX) if (i == IWL_RATE_9M_INDEX)
continue; continue;
/* Disable MCS9 as a workaround */
if (i == IWL_RATE_MCS_9_INDEX)
continue;
/* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */
if (i == IWL_RATE_MCS_9_INDEX && if (i == IWL_RATE_MCS_9_INDEX &&
sta->bandwidth == IEEE80211_STA_RX_BW_20) sta->bandwidth == IEEE80211_STA_RX_BW_20)
...@@ -2511,10 +2458,6 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta, ...@@ -2511,10 +2458,6 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
if (i == IWL_RATE_9M_INDEX) if (i == IWL_RATE_9M_INDEX)
continue; continue;
/* Disable MCS9 as a workaround */
if (i == IWL_RATE_MCS_9_INDEX)
continue;
/* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */
if (i == IWL_RATE_MCS_9_INDEX && if (i == IWL_RATE_MCS_9_INDEX &&
sta->bandwidth == IEEE80211_STA_RX_BW_20) sta->bandwidth == IEEE80211_STA_RX_BW_20)
......
...@@ -277,51 +277,22 @@ static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, ...@@ -277,51 +277,22 @@ static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm,
IEEE80211_IFACE_ITER_NORMAL, IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_scan_condition_iterator, iwl_mvm_scan_condition_iterator,
&global_bound); &global_bound);
/*
* Under low latency traffic passive scan is fragmented meaning
* that dwell on a particular channel will be fragmented. Each fragment
* dwell time is 20ms and fragments period is 105ms. Skipping to next
* channel will be delayed by the same period - 105ms. So suspend_time
* parameter describing both fragments and channels skipping periods is
* set to 105ms. This value is chosen so that overall passive scan
* duration will not be too long. Max_out_time in this case is set to
* 70ms, so for active scanning operating channel will be left for 70ms
* while for passive still for 20ms (fragment dwell).
*/
if (global_bound) {
if (!iwl_mvm_low_latency(mvm)) {
params->suspend_time = ieee80211_tu_to_usec(100);
params->max_out_time = ieee80211_tu_to_usec(600);
} else {
params->suspend_time = ieee80211_tu_to_usec(105);
/* P2P doesn't support fragmented passive scan, so
* configure max_out_time to be at least longest dwell
* time for passive scan.
*/
if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p) {
params->max_out_time = ieee80211_tu_to_usec(70);
params->passive_fragmented = true;
} else {
u32 passive_dwell;
/* if (!global_bound)
* Use band G so that passive channel dwell time goto not_bound;
* will be assigned with maximum value.
*/ params->suspend_time = 100;
band = IEEE80211_BAND_2GHZ; params->max_out_time = 600;
passive_dwell = iwl_mvm_get_passive_dwell(band);
params->max_out_time = if (iwl_mvm_low_latency(mvm)) {
ieee80211_tu_to_usec(passive_dwell); params->suspend_time = 250;
} params->max_out_time = 250;
}
} }
not_bound:
for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
if (params->passive_fragmented) params->dwell[band].passive = iwl_mvm_get_passive_dwell(band);
params->dwell[band].passive = 20;
else
params->dwell[band].passive =
iwl_mvm_get_passive_dwell(band);
params->dwell[band].active = iwl_mvm_get_active_dwell(band, params->dwell[band].active = iwl_mvm_get_active_dwell(band,
n_ssids); n_ssids);
} }
......
...@@ -519,6 +519,7 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) ...@@ -519,6 +519,7 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
iwl_mvm_dump_umac_error_log(mvm); iwl_mvm_dump_umac_error_log(mvm);
} }
#ifdef CONFIG_IWLWIFI_DEBUGFS
void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm) void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm)
{ {
const struct fw_img *img; const struct fw_img *img;
...@@ -581,6 +582,7 @@ void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm) ...@@ -581,6 +582,7 @@ void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm)
} }
iwl_trans_release_nic_access(mvm->trans, &flags); iwl_trans_release_nic_access(mvm->trans, &flags);
} }
#endif
/** /**
* iwl_mvm_send_lq_cmd() - Send link quality command * iwl_mvm_send_lq_cmd() - Send link quality command
......
...@@ -117,21 +117,19 @@ struct iwl_dma_ptr { ...@@ -117,21 +117,19 @@ struct iwl_dma_ptr {
/** /**
* iwl_queue_inc_wrap - increment queue index, wrap back to beginning * iwl_queue_inc_wrap - increment queue index, wrap back to beginning
* @index -- current index * @index -- current index
* @n_bd -- total number of entries in queue (must be power of 2)
*/ */
static inline int iwl_queue_inc_wrap(int index, int n_bd) static inline int iwl_queue_inc_wrap(int index)
{ {
return ++index & (n_bd - 1); return ++index & (TFD_QUEUE_SIZE_MAX - 1);
} }
/** /**
* iwl_queue_dec_wrap - decrement queue index, wrap back to end * iwl_queue_dec_wrap - decrement queue index, wrap back to end
* @index -- current index * @index -- current index
* @n_bd -- total number of entries in queue (must be power of 2)
*/ */
static inline int iwl_queue_dec_wrap(int index, int n_bd) static inline int iwl_queue_dec_wrap(int index)
{ {
return --index & (n_bd - 1); return --index & (TFD_QUEUE_SIZE_MAX - 1);
} }
struct iwl_cmd_meta { struct iwl_cmd_meta {
...@@ -145,13 +143,13 @@ struct iwl_cmd_meta { ...@@ -145,13 +143,13 @@ struct iwl_cmd_meta {
* *
* Contains common data for Rx and Tx queues. * Contains common data for Rx and Tx queues.
* *
* Note the difference between n_bd and n_window: the hardware * Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware
* always assumes 256 descriptors, so n_bd is always 256 (unless * always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless
* there might be HW changes in the future). For the normal TX * there might be HW changes in the future). For the normal TX
* queues, n_window, which is the size of the software queue data * queues, n_window, which is the size of the software queue data
* is also 256; however, for the command queue, n_window is only * is also 256; however, for the command queue, n_window is only
* 32 since we don't need so many commands pending. Since the HW * 32 since we don't need so many commands pending. Since the HW
* still uses 256 BDs for DMA though, n_bd stays 256. As a result, * still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256. As a result,
* the software buffers (in the variables @meta, @txb in struct * the software buffers (in the variables @meta, @txb in struct
* iwl_txq) only have 32 entries, while the HW buffers (@tfds in * iwl_txq) only have 32 entries, while the HW buffers (@tfds in
* the same struct) have 256. * the same struct) have 256.
...@@ -162,7 +160,6 @@ struct iwl_cmd_meta { ...@@ -162,7 +160,6 @@ struct iwl_cmd_meta {
* data is a window overlayed over the HW queue. * data is a window overlayed over the HW queue.
*/ */
struct iwl_queue { struct iwl_queue {
int n_bd; /* number of BDs in this queue */
int write_ptr; /* 1-st empty entry (index) host_w*/ int write_ptr; /* 1-st empty entry (index) host_w*/
int read_ptr; /* last used entry (index) host_r*/ int read_ptr; /* last used entry (index) host_r*/
/* use for monitoring and recovering the stuck queue */ /* use for monitoring and recovering the stuck queue */
...@@ -373,6 +370,13 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, ...@@ -373,6 +370,13 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
struct sk_buff_head *skbs); struct sk_buff_head *skbs);
void iwl_trans_pcie_tx_reset(struct iwl_trans *trans); void iwl_trans_pcie_tx_reset(struct iwl_trans *trans);
static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx)
{
struct iwl_tfd_tb *tb = &tfd->tbs[idx];
return le16_to_cpu(tb->hi_n_len) >> 4;
}
/***************************************************** /*****************************************************
* Error handling * Error handling
******************************************************/ ******************************************************/
......
...@@ -850,7 +850,7 @@ static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans) ...@@ -850,7 +850,7 @@ static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans)
trans_pcie->ict_index, read); trans_pcie->ict_index, read);
trans_pcie->ict_tbl[trans_pcie->ict_index] = 0; trans_pcie->ict_tbl[trans_pcie->ict_index] = 0;
trans_pcie->ict_index = trans_pcie->ict_index =
iwl_queue_inc_wrap(trans_pcie->ict_index, ICT_COUNT); ((trans_pcie->ict_index + 1) & (ICT_COUNT - 1));
read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]);
trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index,
......
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
#include "iwl-csr.h" #include "iwl-csr.h"
#include "iwl-prph.h" #include "iwl-prph.h"
#include "iwl-agn-hw.h" #include "iwl-agn-hw.h"
#include "iwl-fw-error-dump.h"
#include "internal.h" #include "internal.h"
static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg) static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg)
...@@ -1337,8 +1338,8 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm) ...@@ -1337,8 +1338,8 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm)
IWL_ERR(trans, IWL_ERR(trans,
"Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n",
cnt, active ? "" : "in", fifo, tbl_dw, cnt, active ? "" : "in", fifo, tbl_dw,
iwl_read_prph(trans, iwl_read_prph(trans, SCD_QUEUE_RDPTR(cnt)) &
SCD_QUEUE_RDPTR(cnt)) & (txq->q.n_bd - 1), (TFD_QUEUE_SIZE_MAX - 1),
iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt))); iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt)));
} }
...@@ -1669,6 +1670,61 @@ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, ...@@ -1669,6 +1670,61 @@ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
IWL_ERR(trans, "failed to create the trans debugfs entry\n"); IWL_ERR(trans, "failed to create the trans debugfs entry\n");
return -ENOMEM; return -ENOMEM;
} }
static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd)
{
u32 cmdlen = 0;
int i;
for (i = 0; i < IWL_NUM_OF_TBS; i++)
cmdlen += iwl_pcie_tfd_tb_get_len(tfd, i);
return cmdlen;
}
static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
void *buf, u32 buflen)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_fw_error_dump_data *data;
struct iwl_txq *cmdq = &trans_pcie->txq[trans_pcie->cmd_queue];
struct iwl_fw_error_dump_txcmd *txcmd;
u32 len;
int i, ptr;
if (!buf)
return sizeof(*data) +
cmdq->q.n_window * (sizeof(*txcmd) +
TFD_MAX_PAYLOAD_SIZE);
len = 0;
data = buf;
data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD);
txcmd = (void *)data->data;
spin_lock_bh(&cmdq->lock);
ptr = cmdq->q.write_ptr;
for (i = 0; i < cmdq->q.n_window; i++) {
u8 idx = get_cmd_index(&cmdq->q, ptr);
u32 caplen, cmdlen;
cmdlen = iwl_trans_pcie_get_cmdlen(&cmdq->tfds[ptr]);
caplen = min_t(u32, TFD_MAX_PAYLOAD_SIZE, cmdlen);
if (cmdlen) {
len += sizeof(*txcmd) + caplen;
txcmd->cmdlen = cpu_to_le32(cmdlen);
txcmd->caplen = cpu_to_le32(caplen);
memcpy(txcmd->data, cmdq->entries[idx].cmd, caplen);
txcmd = (void *)((u8 *)txcmd->data + caplen);
}
ptr = iwl_queue_dec_wrap(ptr);
}
spin_unlock_bh(&cmdq->lock);
data->len = cpu_to_le32(len);
return sizeof(*data) + len;
}
#else #else
static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
struct dentry *dir) struct dentry *dir)
...@@ -1711,6 +1767,10 @@ static const struct iwl_trans_ops trans_ops_pcie = { ...@@ -1711,6 +1767,10 @@ static const struct iwl_trans_ops trans_ops_pcie = {
.grab_nic_access = iwl_trans_pcie_grab_nic_access, .grab_nic_access = iwl_trans_pcie_grab_nic_access,
.release_nic_access = iwl_trans_pcie_release_nic_access, .release_nic_access = iwl_trans_pcie_release_nic_access,
.set_bits_mask = iwl_trans_pcie_set_bits_mask, .set_bits_mask = iwl_trans_pcie_set_bits_mask,
#ifdef CONFIG_IWLWIFI_DEBUGFS
.dump_data = iwl_trans_pcie_dump_data,
#endif
}; };
struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
......
...@@ -70,20 +70,20 @@ static int iwl_queue_space(const struct iwl_queue *q) ...@@ -70,20 +70,20 @@ static int iwl_queue_space(const struct iwl_queue *q)
/* /*
* To avoid ambiguity between empty and completely full queues, there * To avoid ambiguity between empty and completely full queues, there
* should always be less than q->n_bd elements in the queue. * should always be less than TFD_QUEUE_SIZE_MAX elements in the queue.
* If q->n_window is smaller than q->n_bd, there is no need to reserve * If q->n_window is smaller than TFD_QUEUE_SIZE_MAX, there is no need
* any queue entries for this purpose. * to reserve any queue entries for this purpose.
*/ */
if (q->n_window < q->n_bd) if (q->n_window < TFD_QUEUE_SIZE_MAX)
max = q->n_window; max = q->n_window;
else else
max = q->n_bd - 1; max = TFD_QUEUE_SIZE_MAX - 1;
/* /*
* q->n_bd is a power of 2, so the following is equivalent to modulo by * TFD_QUEUE_SIZE_MAX is a power of 2, so the following is equivalent to
* q->n_bd and is well defined for negative dividends. * modulo by TFD_QUEUE_SIZE_MAX and is well defined.
*/ */
used = (q->write_ptr - q->read_ptr) & (q->n_bd - 1); used = (q->write_ptr - q->read_ptr) & (TFD_QUEUE_SIZE_MAX - 1);
if (WARN_ON(used > max)) if (WARN_ON(used > max))
return 0; return 0;
...@@ -94,17 +94,11 @@ static int iwl_queue_space(const struct iwl_queue *q) ...@@ -94,17 +94,11 @@ static int iwl_queue_space(const struct iwl_queue *q)
/* /*
* iwl_queue_init - Initialize queue's high/low-water and read/write indexes * iwl_queue_init - Initialize queue's high/low-water and read/write indexes
*/ */
static int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id) static int iwl_queue_init(struct iwl_queue *q, int slots_num, u32 id)
{ {
q->n_bd = count;
q->n_window = slots_num; q->n_window = slots_num;
q->id = id; q->id = id;
/* count must be power-of-two size, otherwise iwl_queue_inc_wrap
* and iwl_queue_dec_wrap are broken. */
if (WARN_ON(!is_power_of_2(count)))
return -EINVAL;
/* slots_num must be power-of-two size, otherwise /* slots_num must be power-of-two size, otherwise
* get_cmd_index is broken. */ * get_cmd_index is broken. */
if (WARN_ON(!is_power_of_2(slots_num))) if (WARN_ON(!is_power_of_2(slots_num)))
...@@ -197,13 +191,13 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data) ...@@ -197,13 +191,13 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data)
IWL_ERR(trans, IWL_ERR(trans,
"Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n",
i, active ? "" : "in", fifo, tbl_dw, i, active ? "" : "in", fifo, tbl_dw,
iwl_read_prph(trans, iwl_read_prph(trans, SCD_QUEUE_RDPTR(i)) &
SCD_QUEUE_RDPTR(i)) & (txq->q.n_bd - 1), (TFD_QUEUE_SIZE_MAX - 1),
iwl_read_prph(trans, SCD_QUEUE_WRPTR(i))); iwl_read_prph(trans, SCD_QUEUE_WRPTR(i)));
} }
for (i = q->read_ptr; i != q->write_ptr; for (i = q->read_ptr; i != q->write_ptr;
i = iwl_queue_inc_wrap(i, q->n_bd)) i = iwl_queue_inc_wrap(i))
IWL_ERR(trans, "scratch %d = 0x%08x\n", i, IWL_ERR(trans, "scratch %d = 0x%08x\n", i,
le32_to_cpu(txq->scratchbufs[i].scratch)); le32_to_cpu(txq->scratchbufs[i].scratch));
...@@ -359,13 +353,6 @@ static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx) ...@@ -359,13 +353,6 @@ static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx)
return addr; return addr;
} }
static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx)
{
struct iwl_tfd_tb *tb = &tfd->tbs[idx];
return le16_to_cpu(tb->hi_n_len) >> 4;
}
static inline void iwl_pcie_tfd_set_tb(struct iwl_tfd *tfd, u8 idx, static inline void iwl_pcie_tfd_set_tb(struct iwl_tfd *tfd, u8 idx,
dma_addr_t addr, u16 len) dma_addr_t addr, u16 len)
{ {
...@@ -425,13 +412,17 @@ static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) ...@@ -425,13 +412,17 @@ static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)
{ {
struct iwl_tfd *tfd_tmp = txq->tfds; struct iwl_tfd *tfd_tmp = txq->tfds;
/* rd_ptr is bounded by n_bd and idx is bounded by n_window */ /* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and
* idx is bounded by n_window
*/
int rd_ptr = txq->q.read_ptr; int rd_ptr = txq->q.read_ptr;
int idx = get_cmd_index(&txq->q, rd_ptr); int idx = get_cmd_index(&txq->q, rd_ptr);
lockdep_assert_held(&txq->lock); lockdep_assert_held(&txq->lock);
/* We have only q->n_window txq->entries, but we use q->n_bd tfds */ /* We have only q->n_window txq->entries, but we use
* TFD_QUEUE_SIZE_MAX tfds
*/
iwl_pcie_tfd_unmap(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr]); iwl_pcie_tfd_unmap(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr]);
/* free SKB */ /* free SKB */
...@@ -452,7 +443,7 @@ static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) ...@@ -452,7 +443,7 @@ static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)
} }
static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq,
dma_addr_t addr, u16 len, u8 reset) dma_addr_t addr, u16 len, bool reset)
{ {
struct iwl_queue *q; struct iwl_queue *q;
struct iwl_tfd *tfd, *tfd_tmp; struct iwl_tfd *tfd, *tfd_tmp;
...@@ -565,8 +556,7 @@ static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, ...@@ -565,8 +556,7 @@ static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq,
BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
/* Initialize queue's high/low-water marks, and head/tail indexes */ /* Initialize queue's high/low-water marks, and head/tail indexes */
ret = iwl_queue_init(&txq->q, TFD_QUEUE_SIZE_MAX, slots_num, ret = iwl_queue_init(&txq->q, slots_num, txq_id);
txq_id);
if (ret) if (ret)
return ret; return ret;
...@@ -591,15 +581,12 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) ...@@ -591,15 +581,12 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)
struct iwl_txq *txq = &trans_pcie->txq[txq_id]; struct iwl_txq *txq = &trans_pcie->txq[txq_id];
struct iwl_queue *q = &txq->q; struct iwl_queue *q = &txq->q;
if (!q->n_bd)
return;
spin_lock_bh(&txq->lock); spin_lock_bh(&txq->lock);
while (q->write_ptr != q->read_ptr) { while (q->write_ptr != q->read_ptr) {
IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n", IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n",
txq_id, q->read_ptr); txq_id, q->read_ptr);
iwl_pcie_txq_free_tfd(trans, txq); iwl_pcie_txq_free_tfd(trans, txq);
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); q->read_ptr = iwl_queue_inc_wrap(q->read_ptr);
} }
txq->active = false; txq->active = false;
spin_unlock_bh(&txq->lock); spin_unlock_bh(&txq->lock);
...@@ -636,10 +623,12 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id) ...@@ -636,10 +623,12 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id)
} }
/* De-alloc circular buffer of TFDs */ /* De-alloc circular buffer of TFDs */
if (txq->q.n_bd) { if (txq->tfds) {
dma_free_coherent(dev, sizeof(struct iwl_tfd) * dma_free_coherent(dev,
txq->q.n_bd, txq->tfds, txq->q.dma_addr); sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX,
txq->tfds, txq->q.dma_addr);
txq->q.dma_addr = 0; txq->q.dma_addr = 0;
txq->tfds = NULL;
dma_free_coherent(dev, dma_free_coherent(dev,
sizeof(*txq->scratchbufs) * txq->q.n_window, sizeof(*txq->scratchbufs) * txq->q.n_window,
...@@ -948,8 +937,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, ...@@ -948,8 +937,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
{ {
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_txq *txq = &trans_pcie->txq[txq_id]; struct iwl_txq *txq = &trans_pcie->txq[txq_id];
/* n_bd is usually 256 => n_bd - 1 = 0xff */ int tfd_num = ssn & (TFD_QUEUE_SIZE_MAX - 1);
int tfd_num = ssn & (txq->q.n_bd - 1);
struct iwl_queue *q = &txq->q; struct iwl_queue *q = &txq->q;
int last_to_free; int last_to_free;
...@@ -973,12 +961,12 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, ...@@ -973,12 +961,12 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
/*Since we free until index _not_ inclusive, the one before index is /*Since we free until index _not_ inclusive, the one before index is
* the last we will free. This one must be used */ * the last we will free. This one must be used */
last_to_free = iwl_queue_dec_wrap(tfd_num, q->n_bd); last_to_free = iwl_queue_dec_wrap(tfd_num);
if (!iwl_queue_used(q, last_to_free)) { if (!iwl_queue_used(q, last_to_free)) {
IWL_ERR(trans, IWL_ERR(trans,
"%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n", "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n",
__func__, txq_id, last_to_free, q->n_bd, __func__, txq_id, last_to_free, TFD_QUEUE_SIZE_MAX,
q->write_ptr, q->read_ptr); q->write_ptr, q->read_ptr);
goto out; goto out;
} }
...@@ -988,7 +976,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, ...@@ -988,7 +976,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
for (; for (;
q->read_ptr != tfd_num; q->read_ptr != tfd_num;
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) {
if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL)) if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL))
continue; continue;
...@@ -1027,16 +1015,16 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx) ...@@ -1027,16 +1015,16 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
lockdep_assert_held(&txq->lock); lockdep_assert_held(&txq->lock);
if ((idx >= q->n_bd) || (!iwl_queue_used(q, idx))) { if ((idx >= TFD_QUEUE_SIZE_MAX) || (!iwl_queue_used(q, idx))) {
IWL_ERR(trans, IWL_ERR(trans,
"%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n", "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n",
__func__, txq_id, idx, q->n_bd, __func__, txq_id, idx, TFD_QUEUE_SIZE_MAX,
q->write_ptr, q->read_ptr); q->write_ptr, q->read_ptr);
return; return;
} }
for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; for (idx = iwl_queue_inc_wrap(idx); q->read_ptr != idx;
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) {
if (nfreed++ > 0) { if (nfreed++ > 0) {
IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n", IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n",
...@@ -1327,28 +1315,39 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, ...@@ -1327,28 +1315,39 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
cmd_pos = offsetof(struct iwl_device_cmd, payload); cmd_pos = offsetof(struct iwl_device_cmd, payload);
copy_size = sizeof(out_cmd->hdr); copy_size = sizeof(out_cmd->hdr);
for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {
int copy = 0; int copy;
if (!cmd->len[i]) if (!cmd->len[i])
continue; continue;
/* need at least IWL_HCMD_SCRATCHBUF_SIZE copied */
if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) {
copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size;
if (copy > cmd->len[i])
copy = cmd->len[i];
}
/* copy everything if not nocopy/dup */ /* copy everything if not nocopy/dup */
if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY |
IWL_HCMD_DFL_DUP))) IWL_HCMD_DFL_DUP))) {
copy = cmd->len[i]; copy = cmd->len[i];
if (copy) {
memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy); memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy);
cmd_pos += copy; cmd_pos += copy;
copy_size += copy; copy_size += copy;
continue;
}
/*
* Otherwise we need at least IWL_HCMD_SCRATCHBUF_SIZE copied
* in total (for the scratchbuf handling), 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]);
memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy);
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 > cmd->len[i])
copy = cmd->len[i];
copy_size += copy;
} }
} }
...@@ -1363,7 +1362,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, ...@@ -1363,7 +1362,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size); memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size);
iwl_pcie_txq_build_tfd(trans, txq, iwl_pcie_txq_build_tfd(trans, txq,
iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr), iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr),
scratch_size, 1); scratch_size, true);
/* map first command fragment, if any remains */ /* map first command fragment, if any remains */
if (copy_size > scratch_size) { if (copy_size > scratch_size) {
...@@ -1379,7 +1378,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, ...@@ -1379,7 +1378,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
} }
iwl_pcie_txq_build_tfd(trans, txq, phys_addr, iwl_pcie_txq_build_tfd(trans, txq, phys_addr,
copy_size - scratch_size, 0); copy_size - scratch_size, false);
} }
/* map the remaining (adjusted) nocopy/dup fragments */ /* map the remaining (adjusted) nocopy/dup fragments */
...@@ -1402,7 +1401,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, ...@@ -1402,7 +1401,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
goto out; goto out;
} }
iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], 0); iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], false);
} }
out_meta->flags = cmd->flags; out_meta->flags = cmd->flags;
...@@ -1445,7 +1444,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, ...@@ -1445,7 +1444,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
} }
/* Increment and update queue's write index */ /* Increment and update queue's write index */
q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); q->write_ptr = iwl_queue_inc_wrap(q->write_ptr);
iwl_pcie_txq_inc_wr_ptr(trans, txq); iwl_pcie_txq_inc_wr_ptr(trans, txq);
spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
...@@ -1740,7 +1739,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -1740,7 +1739,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
memcpy(&txq->scratchbufs[q->write_ptr], &dev_cmd->hdr, memcpy(&txq->scratchbufs[q->write_ptr], &dev_cmd->hdr,
IWL_HCMD_SCRATCHBUF_SIZE); IWL_HCMD_SCRATCHBUF_SIZE);
iwl_pcie_txq_build_tfd(trans, txq, tb0_phys, iwl_pcie_txq_build_tfd(trans, txq, tb0_phys,
IWL_HCMD_SCRATCHBUF_SIZE, 1); IWL_HCMD_SCRATCHBUF_SIZE, true);
/* there must be data left over for TB1 or this code must be changed */ /* 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_HCMD_SCRATCHBUF_SIZE);
...@@ -1750,7 +1749,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -1750,7 +1749,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
tb1_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE); tb1_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(trans->dev, tb1_phys))) if (unlikely(dma_mapping_error(trans->dev, tb1_phys)))
goto out_err; goto out_err;
iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, 0); iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false);
/* /*
* Set up TFD's third entry to point directly to remainder * Set up TFD's third entry to point directly to remainder
...@@ -1766,7 +1765,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -1766,7 +1765,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
&txq->tfds[q->write_ptr]); &txq->tfds[q->write_ptr]);
goto out_err; goto out_err;
} }
iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, 0); iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false);
} }
/* Set up entry for this TFD in Tx byte-count array */ /* Set up entry for this TFD in Tx byte-count array */
...@@ -1788,7 +1787,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, ...@@ -1788,7 +1787,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
/* Tell device the write index *just past* this latest filled TFD */ /* Tell device the write index *just past* this latest filled TFD */
q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); q->write_ptr = iwl_queue_inc_wrap(q->write_ptr);
if (!wait_write_ptr) if (!wait_write_ptr)
iwl_pcie_txq_inc_wr_ptr(trans, txq); iwl_pcie_txq_inc_wr_ptr(trans, txq);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment