aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/iwlwifi
diff options
context:
space:
mode:
authorJohn W. Linville <linville@tuxdriver.com>2012-03-16 13:45:25 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-03-16 13:45:25 -0400
commit01a282980937f9ca55a3cb06b9c6ff1cc49ea396 (patch)
tree07a043edc861e245a7a9764751af0898a1f1199a /drivers/net/wireless/iwlwifi
parentd5ddb4a59ed43b4c569b4efa8b508d50ef140cc6 (diff)
parent377526578f2c343ea281a918b18ece1fca65005c (diff)
downloadlinux-stericsson-01a282980937f9ca55a3cb06b9c6ff1cc49ea396.tar.gz
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem
Conflicts: drivers/net/wireless/ath/ath9k/hw.c
Diffstat (limited to 'drivers/net/wireless/iwlwifi')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-lib.c1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rx.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rxon.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c14
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.c5
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debugfs.c14
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-dev.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-drv.c430
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw-file.h5
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw.h49
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-mac80211.c15
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-shared.h20
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-testmode.c27
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c31
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie.c44
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h40
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-ucode.c39
19 files changed, 512 insertions, 234 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
index f98baaba0c2a..56f41c9409d1 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c
@@ -1189,6 +1189,7 @@ int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan)
memcpy(&rxon, &ctx->active, sizeof(rxon));
+ priv->ucode_loaded = false;
iwl_trans_stop_device(trans(priv));
priv->wowlan = true;
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c
index cc0227c1f352..44c6f712b77d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c
@@ -671,7 +671,7 @@ static int iwlagn_rx_card_state_notif(struct iwl_priv *priv,
wiphy_rfkill_set_hw_state(priv->hw->wiphy,
test_bit(STATUS_RF_KILL_HW, &priv->status));
else
- wake_up(&priv->shrd->wait_command_queue);
+ wake_up(&trans(priv)->wait_command_queue);
return 0;
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
index 36909077f994..2e1a31797a9e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
@@ -822,7 +822,7 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
if (changes & BSS_CHANGED_ASSOC) {
if (bss_conf->assoc) {
- priv->timestamp = bss_conf->timestamp;
+ priv->timestamp = bss_conf->last_tsf;
ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK;
} else {
/*
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 28422c03d673..f1226dbf789d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -816,6 +816,7 @@ void iwl_down(struct iwl_priv *priv)
if (priv->mac80211_registered)
ieee80211_stop_queues(priv->hw);
+ priv->ucode_loaded = false;
iwl_trans_stop_device(trans(priv));
/* Clear out all status bits but a few that are stable across reset */
@@ -962,8 +963,6 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
{
priv->workqueue = create_singlethread_workqueue(DRV_NAME);
- init_waitqueue_head(&priv->shrd->wait_command_queue);
-
INIT_WORK(&priv->restart, iwl_bg_restart);
INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update);
INIT_WORK(&priv->run_time_calib_work, iwl_bg_run_time_calib_work);
@@ -1186,6 +1185,14 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
u16 num_mac;
u32 ucode_flags;
struct iwl_trans_config trans_cfg;
+ static const u8 no_reclaim_cmds[] = {
+ REPLY_RX_PHY_CMD,
+ REPLY_RX,
+ REPLY_RX_MPDU_CMD,
+ REPLY_COMPRESSED_BA,
+ STATISTICS_NOTIFICATION,
+ REPLY_TX,
+ };
/************************
* 1. Allocating HW data
@@ -1211,6 +1218,8 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
* to know about.
*/
trans_cfg.op_mode = op_mode;
+ trans_cfg.no_reclaim_cmds = no_reclaim_cmds;
+ trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
ucode_flags = fw->ucode_capa.flags;
@@ -1398,6 +1407,7 @@ static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode)
iwl_tt_exit(priv);
/*This will stop the queues, move the device to low power state */
+ priv->ucode_loaded = false;
iwl_trans_stop_device(trans(priv));
iwl_eeprom_free(priv->shrd);
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index 8b85940731f2..46490d3b95b9 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -838,6 +838,9 @@ static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
iwl_print_rx_config_cmd(priv, IWL_RXON_CTX_BSS);
#endif
+ /* uCode is no longer loaded. */
+ priv->ucode_loaded = false;
+
/* Set the FW error flag -- cleared on iwl_down */
set_bit(STATUS_FW_ERROR, &priv->shrd->status);
@@ -850,7 +853,7 @@ static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand)
* commands by clearing the ready bit */
clear_bit(STATUS_READY, &priv->status);
- wake_up(&priv->shrd->wait_command_queue);
+ wake_up(&trans(priv)->wait_command_queue);
if (!ondemand) {
/*
diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
index 9b71c87847c2..b7b1c04f2fba 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
@@ -230,15 +230,18 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file,
int pos = 0;
int sram;
struct iwl_priv *priv = file->private_data;
+ const struct fw_img *img;
size_t bufsz;
/* default is to dump the entire data segment */
if (!priv->dbgfs_sram_offset && !priv->dbgfs_sram_len) {
priv->dbgfs_sram_offset = 0x800000;
- if (priv->shrd->ucode_type == IWL_UCODE_INIT)
- priv->dbgfs_sram_len = priv->fw->ucode_init.data.len;
- else
- priv->dbgfs_sram_len = priv->fw->ucode_rt.data.len;
+ if (!priv->ucode_loaded) {
+ IWL_ERR(priv, "No uCode has been loadded.\n");
+ return -EINVAL;
+ }
+ img = &priv->fw->img[priv->shrd->ucode_type];
+ priv->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
}
len = priv->dbgfs_sram_len;
@@ -335,13 +338,14 @@ static ssize_t iwl_dbgfs_wowlan_sram_read(struct file *file,
size_t count, loff_t *ppos)
{
struct iwl_priv *priv = file->private_data;
+ const struct fw_img *img = &priv->fw->img[IWL_UCODE_WOWLAN];
if (!priv->wowlan_sram)
return -ENODATA;
return simple_read_from_buffer(user_buf, count, ppos,
priv->wowlan_sram,
- priv->fw->ucode_wowlan.data.len);
+ img->sec[IWL_UCODE_SECTION_DATA].len);
}
static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index aa4b3b122da4..16956b777f96 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -769,6 +769,8 @@ struct iwl_priv {
/* firmware reload counter and timestamp */
unsigned long reload_jiffies;
int reload_count;
+ bool ucode_loaded;
+ bool init_ucode_run; /* Don't run init uCode again */
/* we allocate array of iwl_channel_info for NIC's valid channels.
* Access via channel # using indirect index array */
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c
index 29a3ae48df6d..6f312c77af5e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.c
@@ -69,6 +69,7 @@
#include "iwl-trans.h"
#include "iwl-shared.h"
#include "iwl-op-mode.h"
+#include "iwl-agn-hw.h"
/* private includes */
#include "iwl-fw-file.h"
@@ -96,6 +97,16 @@ struct iwl_drv {
+/*
+ * struct fw_sec: Just for the image parsing proccess.
+ * For the fw storage we are using struct fw_desc.
+ */
+struct fw_sec {
+ const void *data; /* the sec data */
+ size_t size; /* section size */
+ u32 offset; /* offset of writing in the device */
+};
+
static void iwl_free_fw_desc(struct iwl_drv *drv, struct fw_desc *desc)
{
if (desc->v_addr)
@@ -107,32 +118,34 @@ static void iwl_free_fw_desc(struct iwl_drv *drv, struct fw_desc *desc)
static void iwl_free_fw_img(struct iwl_drv *drv, struct fw_img *img)
{
- iwl_free_fw_desc(drv, &img->code);
- iwl_free_fw_desc(drv, &img->data);
+ int i;
+ for (i = 0; i < IWL_UCODE_SECTION_MAX; i++)
+ iwl_free_fw_desc(drv, &img->sec[i]);
}
static void iwl_dealloc_ucode(struct iwl_drv *drv)
{
- iwl_free_fw_img(drv, &drv->fw.ucode_rt);
- iwl_free_fw_img(drv, &drv->fw.ucode_init);
- iwl_free_fw_img(drv, &drv->fw.ucode_wowlan);
+ int i;
+ for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
+ iwl_free_fw_img(drv, drv->fw.img + i);
}
static int iwl_alloc_fw_desc(struct iwl_drv *drv, struct fw_desc *desc,
- const void *data, size_t len)
+ struct fw_sec *sec)
{
- if (!len) {
+ if (!sec || !sec->size) {
desc->v_addr = NULL;
return -EINVAL;
}
- desc->v_addr = dma_alloc_coherent(trans(drv)->dev, len,
+ desc->v_addr = dma_alloc_coherent(trans(drv)->dev, sec->size,
&desc->p_addr, GFP_KERNEL);
if (!desc->v_addr)
return -ENOMEM;
- desc->len = len;
- memcpy(desc->v_addr, data, len);
+ desc->len = sec->size;
+ desc->offset = sec->offset;
+ memcpy(desc->v_addr, sec->data, sec->size);
return 0;
}
@@ -177,18 +190,123 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first)
GFP_KERNEL, drv, iwl_ucode_callback);
}
-struct iwlagn_firmware_pieces {
- const void *inst, *data, *init, *init_data, *wowlan_inst, *wowlan_data;
- size_t inst_size, data_size, init_size, init_data_size,
- wowlan_inst_size, wowlan_data_size;
+struct fw_img_parsing {
+ struct fw_sec sec[IWL_UCODE_SECTION_MAX];
+ int sec_counter;
+};
+
+/*
+ * struct fw_sec_parsing: to extract fw section and it's offset from tlv
+ */
+struct fw_sec_parsing {
+ __le32 offset;
+ const u8 data[];
+} __packed;
+
+/**
+ * struct iwl_tlv_calib_data - parse the default calib data from TLV
+ *
+ * @ucode_type: the uCode to which the following default calib relates.
+ * @calib: default calibrations.
+ */
+struct iwl_tlv_calib_data {
+ __le32 ucode_type;
+ __le64 calib;
+} __packed;
+
+struct iwl_firmware_pieces {
+ struct fw_img_parsing img[IWL_UCODE_TYPE_MAX];
u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
};
+/*
+ * These functions are just to extract uCode section data from the pieces
+ * structure.
+ */
+static struct fw_sec *get_sec(struct iwl_firmware_pieces *pieces,
+ enum iwl_ucode_type type,
+ int sec)
+{
+ return &pieces->img[type].sec[sec];
+}
+
+static void set_sec_data(struct iwl_firmware_pieces *pieces,
+ enum iwl_ucode_type type,
+ int sec,
+ const void *data)
+{
+ pieces->img[type].sec[sec].data = data;
+}
+
+static void set_sec_size(struct iwl_firmware_pieces *pieces,
+ enum iwl_ucode_type type,
+ int sec,
+ size_t size)
+{
+ pieces->img[type].sec[sec].size = size;
+}
+
+static size_t get_sec_size(struct iwl_firmware_pieces *pieces,
+ enum iwl_ucode_type type,
+ int sec)
+{
+ return pieces->img[type].sec[sec].size;
+}
+
+static void set_sec_offset(struct iwl_firmware_pieces *pieces,
+ enum iwl_ucode_type type,
+ int sec,
+ u32 offset)
+{
+ pieces->img[type].sec[sec].offset = offset;
+}
+
+/*
+ * Gets uCode section from tlv.
+ */
+static int iwl_store_ucode_sec(struct iwl_firmware_pieces *pieces,
+ const void *data, enum iwl_ucode_type type,
+ int size)
+{
+ struct fw_img_parsing *img;
+ struct fw_sec *sec;
+ struct fw_sec_parsing *sec_parse;
+
+ if (WARN_ON(!pieces || !data || type >= IWL_UCODE_TYPE_MAX))
+ return -1;
+
+ sec_parse = (struct fw_sec_parsing *)data;
+
+ img = &pieces->img[type];
+ sec = &img->sec[img->sec_counter];
+
+ sec->offset = le32_to_cpu(sec_parse->offset);
+ sec->data = sec_parse->data;
+
+ ++img->sec_counter;
+
+ return 0;
+}
+
+static int iwl_set_default_calib(struct iwl_drv *drv, const u8 *data)
+{
+ struct iwl_tlv_calib_data *def_calib =
+ (struct iwl_tlv_calib_data *)data;
+ u32 ucode_type = le32_to_cpu(def_calib->ucode_type);
+ if (ucode_type >= IWL_UCODE_TYPE_MAX) {
+ IWL_ERR(drv, "Wrong ucode_type %u for default calibration.\n",
+ ucode_type);
+ return -EINVAL;
+ }
+ drv->fw.default_calib[ucode_type] = le64_to_cpu(def_calib->calib);
+ return 0;
+}
+
static int iwl_parse_v1_v2_firmware(struct iwl_drv *drv,
- const struct firmware *ucode_raw,
- struct iwlagn_firmware_pieces *pieces)
+ const struct firmware *ucode_raw,
+ struct iwl_firmware_pieces *pieces)
{
struct iwl_ucode_header *ucode = (void *)ucode_raw->data;
u32 api_ver, hdr_size, build;
@@ -206,11 +324,14 @@ static int iwl_parse_v1_v2_firmware(struct iwl_drv *drv,
return -EINVAL;
}
build = le32_to_cpu(ucode->u.v2.build);
- pieces->inst_size = le32_to_cpu(ucode->u.v2.inst_size);
- pieces->data_size = le32_to_cpu(ucode->u.v2.data_size);
- pieces->init_size = le32_to_cpu(ucode->u.v2.init_size);
- pieces->init_data_size =
- le32_to_cpu(ucode->u.v2.init_data_size);
+ set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST,
+ le32_to_cpu(ucode->u.v2.inst_size));
+ set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA,
+ le32_to_cpu(ucode->u.v2.data_size));
+ set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST,
+ le32_to_cpu(ucode->u.v2.init_size));
+ set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA,
+ le32_to_cpu(ucode->u.v2.init_data_size));
src = ucode->u.v2.data;
break;
case 0:
@@ -222,11 +343,14 @@ static int iwl_parse_v1_v2_firmware(struct iwl_drv *drv,
return -EINVAL;
}
build = 0;
- pieces->inst_size = le32_to_cpu(ucode->u.v1.inst_size);
- pieces->data_size = le32_to_cpu(ucode->u.v1.data_size);
- pieces->init_size = le32_to_cpu(ucode->u.v1.init_size);
- pieces->init_data_size =
- le32_to_cpu(ucode->u.v1.init_data_size);
+ set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST,
+ le32_to_cpu(ucode->u.v1.inst_size));
+ set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA,
+ le32_to_cpu(ucode->u.v1.data_size));
+ set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST,
+ le32_to_cpu(ucode->u.v1.init_size));
+ set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA,
+ le32_to_cpu(ucode->u.v1.init_data_size));
src = ucode->u.v1.data;
break;
}
@@ -248,9 +372,12 @@ static int iwl_parse_v1_v2_firmware(struct iwl_drv *drv,
buildstr);
/* Verify size of file vs. image size info in file's header */
- if (ucode_raw->size != hdr_size + pieces->inst_size +
- pieces->data_size + pieces->init_size +
- pieces->init_data_size) {
+
+ if (ucode_raw->size != hdr_size +
+ get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) +
+ get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) +
+ get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) +
+ get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA)) {
IWL_ERR(drv,
"uCode file size %d does not match expected size\n",
@@ -258,21 +385,29 @@ static int iwl_parse_v1_v2_firmware(struct iwl_drv *drv,
return -EINVAL;
}
- pieces->inst = src;
- src += pieces->inst_size;
- pieces->data = src;
- src += pieces->data_size;
- pieces->init = src;
- src += pieces->init_size;
- pieces->init_data = src;
- src += pieces->init_data_size;
+ set_sec_data(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, src);
+ src += get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST);
+ set_sec_offset(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST,
+ IWLAGN_RTC_INST_LOWER_BOUND);
+ set_sec_data(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, src);
+ src += get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA);
+ set_sec_offset(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA,
+ IWLAGN_RTC_DATA_LOWER_BOUND);
+ set_sec_data(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, src);
+ src += get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST);
+ set_sec_offset(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST,
+ IWLAGN_RTC_INST_LOWER_BOUND);
+ set_sec_data(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, src);
+ src += get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA);
+ set_sec_offset(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA,
+ IWLAGN_RTC_DATA_LOWER_BOUND);
return 0;
}
static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
const struct firmware *ucode_raw,
- struct iwlagn_firmware_pieces *pieces,
+ struct iwl_firmware_pieces *pieces,
struct iwl_ucode_capabilities *capa)
{
struct iwl_tlv_ucode_header *ucode = (void *)ucode_raw->data;
@@ -368,20 +503,40 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
switch (tlv_type) {
case IWL_UCODE_TLV_INST:
- pieces->inst = tlv_data;
- pieces->inst_size = tlv_len;
+ set_sec_data(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_INST, tlv_data);
+ set_sec_size(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_INST, tlv_len);
+ set_sec_offset(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_INST,
+ IWLAGN_RTC_INST_LOWER_BOUND);
break;
case IWL_UCODE_TLV_DATA:
- pieces->data = tlv_data;
- pieces->data_size = tlv_len;
+ set_sec_data(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_DATA, tlv_data);
+ set_sec_size(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_DATA, tlv_len);
+ set_sec_offset(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_DATA,
+ IWLAGN_RTC_DATA_LOWER_BOUND);
break;
case IWL_UCODE_TLV_INIT:
- pieces->init = tlv_data;
- pieces->init_size = tlv_len;
+ set_sec_data(pieces, IWL_UCODE_INIT,
+ IWL_UCODE_SECTION_INST, tlv_data);
+ set_sec_size(pieces, IWL_UCODE_INIT,
+ IWL_UCODE_SECTION_INST, tlv_len);
+ set_sec_offset(pieces, IWL_UCODE_INIT,
+ IWL_UCODE_SECTION_INST,
+ IWLAGN_RTC_INST_LOWER_BOUND);
break;
case IWL_UCODE_TLV_INIT_DATA:
- pieces->init_data = tlv_data;
- pieces->init_data_size = tlv_len;
+ set_sec_data(pieces, IWL_UCODE_INIT,
+ IWL_UCODE_SECTION_DATA, tlv_data);
+ set_sec_size(pieces, IWL_UCODE_INIT,
+ IWL_UCODE_SECTION_DATA, tlv_len);
+ set_sec_offset(pieces, IWL_UCODE_INIT,
+ IWL_UCODE_SECTION_DATA,
+ IWLAGN_RTC_DATA_LOWER_BOUND);
break;
case IWL_UCODE_TLV_BOOT:
IWL_ERR(drv, "Found unexpected BOOT ucode\n");
@@ -455,12 +610,22 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
drv->fw.enhance_sensitivity_table = true;
break;
case IWL_UCODE_TLV_WOWLAN_INST:
- pieces->wowlan_inst = tlv_data;
- pieces->wowlan_inst_size = tlv_len;
+ set_sec_data(pieces, IWL_UCODE_WOWLAN,
+ IWL_UCODE_SECTION_INST, tlv_data);
+ set_sec_size(pieces, IWL_UCODE_WOWLAN,
+ IWL_UCODE_SECTION_INST, tlv_len);
+ set_sec_offset(pieces, IWL_UCODE_WOWLAN,
+ IWL_UCODE_SECTION_INST,
+ IWLAGN_RTC_INST_LOWER_BOUND);
break;
case IWL_UCODE_TLV_WOWLAN_DATA:
- pieces->wowlan_data = tlv_data;
- pieces->wowlan_data_size = tlv_len;
+ set_sec_data(pieces, IWL_UCODE_WOWLAN,
+ IWL_UCODE_SECTION_DATA, tlv_data);
+ set_sec_size(pieces, IWL_UCODE_WOWLAN,
+ IWL_UCODE_SECTION_DATA, tlv_len);
+ set_sec_offset(pieces, IWL_UCODE_WOWLAN,
+ IWL_UCODE_SECTION_DATA,
+ IWLAGN_RTC_DATA_LOWER_BOUND);
break;
case IWL_UCODE_TLV_PHY_CALIBRATION_SIZE:
if (tlv_len != sizeof(u32))
@@ -468,6 +633,32 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
capa->standard_phy_calibration_size =
le32_to_cpup((__le32 *)tlv_data);
break;
+ case IWL_UCODE_TLV_SEC_RT:
+ iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR,
+ tlv_len);
+ drv->fw.mvm_fw = true;
+ break;
+ case IWL_UCODE_TLV_SEC_INIT:
+ iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT,
+ tlv_len);
+ drv->fw.mvm_fw = true;
+ break;
+ case IWL_UCODE_TLV_SEC_WOWLAN:
+ iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN,
+ tlv_len);
+ drv->fw.mvm_fw = true;
+ break;
+ case IWL_UCODE_TLV_DEF_CALIB:
+ if (tlv_len != sizeof(struct iwl_tlv_calib_data))
+ goto invalid_tlv_len;
+ if (iwl_set_default_calib(drv, tlv_data))
+ goto tlv_error;
+ break;
+ case IWL_UCODE_TLV_PHY_SKU:
+ if (tlv_len != sizeof(u32))
+ goto invalid_tlv_len;
+ drv->fw.phy_config = le32_to_cpup((__le32 *)tlv_data);
+ break;
default:
IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
break;
@@ -484,11 +675,77 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
invalid_tlv_len:
IWL_ERR(drv, "TLV %d has invalid size: %u\n", tlv_type, tlv_len);
+ tlv_error:
iwl_print_hex_dump(drv, IWL_DL_FW, tlv_data, tlv_len);
return -EINVAL;
}
+static int alloc_pci_desc(struct iwl_drv *drv,
+ struct iwl_firmware_pieces *pieces,
+ enum iwl_ucode_type type)
+{
+ int i;
+ for (i = 0;
+ i < IWL_UCODE_SECTION_MAX && get_sec_size(pieces, type, i);
+ i++)
+ if (iwl_alloc_fw_desc(drv, &(drv->fw.img[type].sec[i]),
+ get_sec(pieces, type, i)))
+ return -1;
+ return 0;
+}
+
+static int validate_sec_sizes(struct iwl_drv *drv,
+ struct iwl_firmware_pieces *pieces,
+ const struct iwl_cfg *cfg)
+{
+ IWL_DEBUG_INFO(drv, "f/w package hdr runtime inst size = %Zd\n",
+ get_sec_size(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_INST));
+ IWL_DEBUG_INFO(drv, "f/w package hdr runtime data size = %Zd\n",
+ get_sec_size(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_DATA));
+ IWL_DEBUG_INFO(drv, "f/w package hdr init inst size = %Zd\n",
+ get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST));
+ IWL_DEBUG_INFO(drv, "f/w package hdr init data size = %Zd\n",
+ get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA));
+
+ /* Verify that uCode images will fit in card's SRAM. */
+ if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) >
+ cfg->max_inst_size) {
+ IWL_ERR(drv, "uCode instr len %Zd too large to fit in\n",
+ get_sec_size(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_INST));
+ return -1;
+ }
+
+ if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) >
+ cfg->max_data_size) {
+ IWL_ERR(drv, "uCode data len %Zd too large to fit in\n",
+ get_sec_size(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_DATA));
+ return -1;
+ }
+
+ if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) >
+ cfg->max_inst_size) {
+ IWL_ERR(drv, "uCode init instr len %Zd too large to fit in\n",
+ get_sec_size(pieces, IWL_UCODE_INIT,
+ IWL_UCODE_SECTION_INST));
+ return -1;
+ }
+
+ if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA) >
+ cfg->max_data_size) {
+ IWL_ERR(drv, "uCode init data len %Zd too large to fit in\n",
+ get_sec_size(pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_DATA));
+ return -1;
+ }
+ return 0;
+}
+
+
/**
* iwl_ucode_callback - callback when firmware was loaded
*
@@ -502,11 +759,12 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
struct iwl_fw *fw = &drv->fw;
struct iwl_ucode_header *ucode;
int err;
- struct iwlagn_firmware_pieces pieces;
+ struct iwl_firmware_pieces pieces;
const unsigned int api_max = cfg->ucode_api_max;
unsigned int api_ok = cfg->ucode_api_ok;
const unsigned int api_min = cfg->ucode_api_min;
u32 api_ver;
+ int i;
fw->ucode_capa.max_probe_length = 200;
fw->ucode_capa.standard_phy_calibration_size =
@@ -588,76 +846,48 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
IWL_DEBUG_INFO(drv, "f/w package hdr ucode version raw = 0x%x\n",
drv->fw.ucode_ver);
IWL_DEBUG_INFO(drv, "f/w package hdr runtime inst size = %Zd\n",
- pieces.inst_size);
+ get_sec_size(&pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_INST));
IWL_DEBUG_INFO(drv, "f/w package hdr runtime data size = %Zd\n",
- pieces.data_size);
+ get_sec_size(&pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_DATA));
IWL_DEBUG_INFO(drv, "f/w package hdr init inst size = %Zd\n",
- pieces.init_size);
+ get_sec_size(&pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST));
IWL_DEBUG_INFO(drv, "f/w package hdr init data size = %Zd\n",
- pieces.init_data_size);
+ get_sec_size(&pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA));
/* Verify that uCode images will fit in card's SRAM */
- if (pieces.inst_size > cfg->max_inst_size) {
+ if (get_sec_size(&pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) >
+ cfg->max_inst_size) {
IWL_ERR(drv, "uCode instr len %Zd too large to fit in\n",
- pieces.inst_size);
+ get_sec_size(&pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_INST));
goto try_again;
}
- if (pieces.data_size > cfg->max_data_size) {
+ if (get_sec_size(&pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) >
+ cfg->max_data_size) {
IWL_ERR(drv, "uCode data len %Zd too large to fit in\n",
- pieces.data_size);
+ get_sec_size(&pieces, IWL_UCODE_REGULAR,
+ IWL_UCODE_SECTION_DATA));
goto try_again;
}
- if (pieces.init_size > cfg->max_inst_size) {
- IWL_ERR(drv, "uCode init instr len %Zd too large to fit in\n",
- pieces.init_size);
- goto try_again;
- }
-
- if (pieces.init_data_size > cfg->max_data_size) {
- IWL_ERR(drv, "uCode init data len %Zd too large to fit in\n",
- pieces.init_data_size);
+ /*
+ * In mvm uCode there is no difference between data and instructions
+ * sections.
+ */
+ if (!fw->mvm_fw && validate_sec_sizes(drv, &pieces, cfg))
goto try_again;
- }
/* Allocate ucode buffers for card's bus-master loading ... */
/* Runtime instructions and 2 copies of data:
* 1) unmodified from disk
* 2) backup cache for save/restore during power-downs */
- if (iwl_alloc_fw_desc(drv, &drv->fw.ucode_rt.code,
- pieces.inst, pieces.inst_size))
- goto err_pci_alloc;
- if (iwl_alloc_fw_desc(drv, &drv->fw.ucode_rt.data,
- pieces.data, pieces.data_size))
- goto err_pci_alloc;
-
- /* Initialization instructions and data */
- if (pieces.init_size && pieces.init_data_size) {
- if (iwl_alloc_fw_desc(drv,
- &drv->fw.ucode_init.code,
- pieces.init, pieces.init_size))
- goto err_pci_alloc;
- if (iwl_alloc_fw_desc(drv,
- &drv->fw.ucode_init.data,
- pieces.init_data, pieces.init_data_size))
+ for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
+ if (alloc_pci_desc(drv, &pieces, i))
goto err_pci_alloc;
- }
-
- /* WoWLAN instructions and data */
- if (pieces.wowlan_inst_size && pieces.wowlan_data_size) {
- if (iwl_alloc_fw_desc(drv,
- &drv->fw.ucode_wowlan.code,
- pieces.wowlan_inst,
- pieces.wowlan_inst_size))
- goto err_pci_alloc;
- if (iwl_alloc_fw_desc(drv,
- &drv->fw.ucode_wowlan.data,
- pieces.wowlan_data,
- pieces.wowlan_data_size))
- goto err_pci_alloc;
- }
/* Now that we can no longer fail, copy information */
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
index 7ca6c9526357..c924ccb93c8c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
@@ -124,6 +124,11 @@ enum iwl_ucode_tlv_type {
IWL_UCODE_TLV_WOWLAN_INST = 16,
IWL_UCODE_TLV_WOWLAN_DATA = 17,
IWL_UCODE_TLV_FLAGS = 18,
+ IWL_UCODE_TLV_SEC_RT = 19,
+ IWL_UCODE_TLV_SEC_INIT = 20,
+ IWL_UCODE_TLV_SEC_WOWLAN = 21,
+ IWL_UCODE_TLV_DEF_CALIB = 22,
+ IWL_UCODE_TLV_PHY_SKU = 23,
};
struct iwl_ucode_tlv {
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h
index 453812a21176..8e36bdc1e522 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw.h
@@ -85,22 +85,52 @@ enum iwl_ucode_tlv_flag {
#define IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE 19
#define IWL_MAX_PHY_CALIBRATE_TBL_SIZE 253
+/**
+ * enum iwl_ucode_type
+ *
+ * The type of ucode.
+ *
+ * @IWL_UCODE_REGULAR: Normal runtime ucode
+ * @IWL_UCODE_INIT: Initial ucode
+ * @IWL_UCODE_WOWLAN: Wake on Wireless enabled ucode
+ */
+enum iwl_ucode_type {
+ IWL_UCODE_REGULAR,
+ IWL_UCODE_INIT,
+ IWL_UCODE_WOWLAN,
+ IWL_UCODE_TYPE_MAX,
+};
+
+/*
+ * enumeration of ucode section.
+ * This enumeration is used for legacy tlv style (before 16.0 uCode).
+ */
+enum iwl_ucode_sec {
+ IWL_UCODE_SECTION_INST,
+ IWL_UCODE_SECTION_DATA,
+};
+/*
+ * For 16.0 uCode and above, there is no differentiation between sections,
+ * just an offset to the HW address.
+ */
+#define IWL_UCODE_SECTION_MAX 4
+
struct iwl_ucode_capabilities {
u32 max_probe_length;
u32 standard_phy_calibration_size;
u32 flags;
};
-/* one for each uCode image (inst/data, boot/init/runtime) */
+/* one for each uCode image (inst/data, init/runtime/wowlan) */
struct fw_desc {
dma_addr_t p_addr; /* hardware address */
void *v_addr; /* software address */
u32 len; /* size in bytes */
+ u32 offset; /* offset in the device */
};
struct fw_img {
- struct fw_desc code; /* firmware code image */
- struct fw_desc data; /* firmware data image */
+ struct fw_desc sec[IWL_UCODE_SECTION_MAX];
};
/* uCode version contains 4 values: Major/Minor/API/Serial */
@@ -114,9 +144,7 @@ struct fw_img {
*
* @ucode_ver: ucode version from the ucode file
* @fw_version: firmware version string
- * @ucode_rt: run time ucode image
- * @ucode_init: init ucode image
- * @ucode_wowlan: wake on wireless ucode image (optional)
+ * @img: ucode image like ucode_rt, ucode_init, ucode_wowlan.
* @ucode_capa: capabilities parsed from the ucode file.
* @enhance_sensitivity_table: device can do enhanced sensitivity.
* @init_evtlog_ptr: event log offset for init ucode.
@@ -132,15 +160,18 @@ struct iwl_fw {
char fw_version[ETHTOOL_BUSINFO_LEN];
/* ucode images */
- struct fw_img ucode_rt;
- struct fw_img ucode_init;
- struct fw_img ucode_wowlan;
+ struct fw_img img[IWL_UCODE_TYPE_MAX];
struct iwl_ucode_capabilities ucode_capa;
bool enhance_sensitivity_table;
u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
+
+ u64 default_calib[IWL_UCODE_TYPE_MAX];
+ u32 phy_config;
+
+ bool mvm_fw;
};
#endif /* __iwl_fw_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-mac80211.c b/drivers/net/wireless/iwlwifi/iwl-mac80211.c
index 9212ee3bef9b..b6805f8e9a01 100644
--- a/drivers/net/wireless/iwlwifi/iwl-mac80211.c
+++ b/drivers/net/wireless/iwlwifi/iwl-mac80211.c
@@ -196,7 +196,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
WIPHY_FLAG_DISABLE_BEACON_HINTS |
WIPHY_FLAG_IBSS_RSN;
- if (priv->fw->ucode_wowlan.code.len &&
+ if (priv->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
trans(priv)->ops->wowlan_suspend &&
device_can_wakeup(trans(priv)->dev)) {
hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
@@ -437,6 +437,7 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
unsigned long flags;
u32 base, status = 0xffffffff;
int ret = -EIO;
+ const struct fw_img *img;
IWL_DEBUG_MAC80211(priv, "enter\n");
mutex_lock(&priv->mutex);
@@ -457,16 +458,18 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
#ifdef CONFIG_IWLWIFI_DEBUGFS
if (ret == 0) {
- if (!priv->wowlan_sram)
+ img = &(priv->fw->img[IWL_UCODE_WOWLAN]);
+ if (!priv->wowlan_sram) {
priv->wowlan_sram =
- kzalloc(priv->fw->ucode_wowlan.data.len,
+ kzalloc(img->sec[IWL_UCODE_SECTION_DATA].len,
GFP_KERNEL);
+ }
if (priv->wowlan_sram)
_iwl_read_targ_mem_words(
- trans(priv), 0x800000,
- priv->wowlan_sram,
- priv->fw->ucode_wowlan.data.len / 4);
+ trans(priv), 0x800000,
+ priv->wowlan_sram,
+ img->sec[IWL_UCODE_SECTION_DATA].len / 4);
}
#endif
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-shared.h b/drivers/net/wireless/iwlwifi/iwl-shared.h
index cf34087aa2fe..b515d657a0ad 100644
--- a/drivers/net/wireless/iwlwifi/iwl-shared.h
+++ b/drivers/net/wireless/iwlwifi/iwl-shared.h
@@ -192,23 +192,6 @@ struct iwl_hw_params {
const struct iwl_sensitivity_ranges *sens;
};
-/**
- * enum iwl_ucode_type
- *
- * The type of ucode currently loaded on the hardware.
- *
- * @IWL_UCODE_NONE: No ucode loaded
- * @IWL_UCODE_REGULAR: Normal runtime ucode
- * @IWL_UCODE_INIT: Initial ucode
- * @IWL_UCODE_WOWLAN: Wake on Wireless enabled ucode
- */
-enum iwl_ucode_type {
- IWL_UCODE_NONE,
- IWL_UCODE_REGULAR,
- IWL_UCODE_INIT,
- IWL_UCODE_WOWLAN,
-};
-
/*
* LED mode
* IWL_LED_DEFAULT: use device default
@@ -376,7 +359,6 @@ struct iwl_cfg {
* @nic: pointer to the nic data
* @hw_params: see struct iwl_hw_params
* @lock: protect general shared data
- * @wait_command_queue: the wait_queue for SYNC host commands
* @eeprom: pointer to the eeprom/OTP image
* @ucode_type: indicator of loaded ucode image
* @device_pointers: pointers to ucode event tables
@@ -391,8 +373,6 @@ struct iwl_shared {
struct iwl_hw_params hw_params;
const struct iwl_fw *fw;
- wait_queue_head_t wait_command_queue;
-
/* eeprom -- this is in the card's little endian byte order */
u8 *eeprom;
diff --git a/drivers/net/wireless/iwlwifi/iwl-testmode.c b/drivers/net/wireless/iwlwifi/iwl-testmode.c
index b06c6763cb7a..76f7f9251436 100644
--- a/drivers/net/wireless/iwlwifi/iwl-testmode.c
+++ b/drivers/net/wireless/iwlwifi/iwl-testmode.c
@@ -466,6 +466,7 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb)
unsigned char *rsp_data_ptr = NULL;
int status = 0, rsp_data_len = 0;
u32 devid, inst_size = 0, data_size = 0;
+ const struct fw_img *img;
switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
case IWL_TM_CMD_APP2DEV_GET_DEVICENAME:
@@ -494,6 +495,7 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb)
case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB:
iwl_testmode_cfg_init_calib(priv);
+ priv->ucode_loaded = false;
iwl_trans_stop_device(trans);
break;
@@ -512,6 +514,7 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb)
case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW:
iwl_scan_cancel_timeout(priv, 200);
+ priv->ucode_loaded = false;
iwl_trans_stop_device(trans);
status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN);
if (status) {
@@ -591,25 +594,13 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb)
IWL_ERR(priv, "Memory allocation fail\n");
return -ENOMEM;
}
- switch (priv->shrd->ucode_type) {
- case IWL_UCODE_REGULAR:
- inst_size = priv->fw->ucode_rt.code.len;
- data_size = priv->fw->ucode_rt.data.len;
- break;
- case IWL_UCODE_INIT:
- inst_size = priv->fw->ucode_init.code.len;
- data_size = priv->fw->ucode_init.data.len;
- break;
- case IWL_UCODE_WOWLAN:
- inst_size = priv->fw->ucode_wowlan.code.len;
- data_size = priv->fw->ucode_wowlan.data.len;
- break;
- case IWL_UCODE_NONE:
+ if (!priv->ucode_loaded) {
IWL_ERR(priv, "No uCode has not been loaded\n");
- break;
- default:
- IWL_ERR(priv, "Unsupported uCode type\n");
- break;
+ return -EINVAL;
+ } else {
+ img = &priv->fw->img[priv->shrd->ucode_type];
+ inst_size = img->sec[IWL_UCODE_SECTION_INST].len;
+ data_size = img->sec[IWL_UCODE_SECTION_DATA].len;
}
NLA_PUT_U32(skb, IWL_TM_ATTR_FW_TYPE, priv->shrd->ucode_type);
NLA_PUT_U32(skb, IWL_TM_ATTR_FW_INST_SIZE, inst_size);
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h
index 67965599bb30..1c2fe87bd7e2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h
@@ -291,6 +291,8 @@ struct iwl_trans_pcie {
wait_queue_head_t ucode_write_waitq;
unsigned long status;
u8 cmd_queue;
+ u8 n_no_reclaim_cmds;
+ u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS];
};
#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c
index 9bc3c73af5e9..8b1a7988e176 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c
@@ -395,13 +395,17 @@ static void iwl_rx_handle_rxbuf(struct iwl_trans *trans,
* there is no command buffer to reclaim.
* Ucode should set SEQ_RX_FRAME bit if ucode-originated,
* but apparently a few don't get set; catch them here. */
- reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME) &&
- (pkt->hdr.cmd != REPLY_RX_PHY_CMD) &&
- (pkt->hdr.cmd != REPLY_RX) &&
- (pkt->hdr.cmd != REPLY_RX_MPDU_CMD) &&
- (pkt->hdr.cmd != REPLY_COMPRESSED_BA) &&
- (pkt->hdr.cmd != STATISTICS_NOTIFICATION) &&
- (pkt->hdr.cmd != REPLY_TX);
+ reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME);
+ if (reclaim) {
+ int i;
+
+ for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) {
+ if (trans_pcie->no_reclaim_cmds[i] == pkt->hdr.cmd) {
+ reclaim = false;
+ break;
+ }
+ }
+ }
sequence = le16_to_cpu(pkt->hdr.sequence);
index = SEQ_TO_INDEX(sequence);
@@ -412,17 +416,6 @@ static void iwl_rx_handle_rxbuf(struct iwl_trans *trans,
else
cmd = NULL;
- /* warn if this is cmd response / notification and the uCode
- * didn't set the SEQ_RX_FRAME for a frame that is
- * uCode-originated
- * If you saw this code after the second half of 2012, then
- * please remove it
- */
- WARN(pkt->hdr.cmd != REPLY_TX && reclaim == false &&
- (!(pkt->hdr.sequence & SEQ_RX_FRAME)),
- "reclaim is false, SEQ_RX_FRAME unset: %s\n",
- get_cmd_string(pkt->hdr.cmd));
-
err = iwl_op_mode_rx(trans->op_mode, &rxcb, cmd);
/*
@@ -691,7 +684,7 @@ static void iwl_irq_handle_error(struct iwl_trans *trans)
*/
clear_bit(STATUS_READY, &trans->shrd->status);
clear_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status);
- wake_up(&trans->shrd->wait_command_queue);
+ wake_up(&trans->wait_command_queue);
IWL_ERR(trans, "RF is used by WiMAX\n");
return;
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c
index eb430b6e1cde..e92972fd6ecf 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c
@@ -928,7 +928,7 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb,
clear_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status);
IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n",
get_cmd_string(cmd->hdr.cmd));
- wake_up(&trans->shrd->wait_command_queue);
+ wake_up(&trans->wait_command_queue);
}
meta->flags = 0;
@@ -992,7 +992,7 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
return ret;
}
- ret = wait_event_timeout(trans->shrd->wait_command_queue,
+ ret = wait_event_timeout(trans->wait_command_queue,
!test_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status),
HOST_COMPLETE_TIMEOUT);
if (!ret) {
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
index 91628565409f..b4f796c82e1e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
@@ -951,12 +951,13 @@ static const u8 iwlagn_pan_ac_to_queue[] = {
/*
* ucode
*/
-static int iwl_load_section(struct iwl_trans *trans, const char *name,
- const struct fw_desc *image, u32 dst_addr)
+static int iwl_load_section(struct iwl_trans *trans, u8 section_num,
+ const struct fw_desc *section)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- dma_addr_t phy_addr = image->p_addr;
- u32 byte_cnt = image->len;
+ dma_addr_t phy_addr = section->p_addr;
+ u32 byte_cnt = section->len;
+ u32 dst_addr = section->offset;
int ret;
trans_pcie->ucode_write_complete = false;
@@ -989,12 +990,13 @@ static int iwl_load_section(struct iwl_trans *trans, const char *name,
FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE |
FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
- IWL_DEBUG_FW(trans, "%s uCode section being loaded...\n", name);
+ IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n",
+ section_num);
ret = wait_event_timeout(trans_pcie->ucode_write_waitq,
trans_pcie->ucode_write_complete, 5 * HZ);
if (!ret) {
- IWL_ERR(trans, "Could not load the %s uCode section\n",
- name);
+ IWL_ERR(trans, "Could not load the [%d] uCode section\n",
+ section_num);
return -ETIMEDOUT;
}
@@ -1005,16 +1007,16 @@ static int iwl_load_given_ucode(struct iwl_trans *trans,
const struct fw_img *image)
{
int ret = 0;
+ int i;
- ret = iwl_load_section(trans, "INST", &image->code,
- IWLAGN_RTC_INST_LOWER_BOUND);
- if (ret)
- return ret;
+ for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) {
+ if (!image->sec[i].p_addr)
+ break;
- ret = iwl_load_section(trans, "DATA", &image->data,
- IWLAGN_RTC_DATA_LOWER_BOUND);
- if (ret)
- return ret;
+ ret = iwl_load_section(trans, i, &image->sec[i]);
+ if (ret)
+ return ret;
+ }
/* Remove all resets to allow NIC to operate */
iwl_write32(trans, CSR_RESET, 0);
@@ -1466,8 +1468,6 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
IWL_DEBUG_TX(trans, "sequence nr = 0X%x\n",
le16_to_cpu(dev_cmd->hdr.sequence));
IWL_DEBUG_TX(trans, "tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags));
- iwl_print_hex_dump(trans, IWL_DL_TX, (u8 *)tx_cmd, sizeof(*tx_cmd));
- iwl_print_hex_dump(trans, IWL_DL_TX, (u8 *)tx_cmd->hdr, hdr_len);
/* Set up entry for this TFD in Tx byte-count array */
iwl_trans_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len));
@@ -1628,6 +1628,13 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans,
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
trans_pcie->cmd_queue = trans_cfg->cmd_queue;
+ if (WARN_ON(trans_cfg->n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS))
+ trans_pcie->n_no_reclaim_cmds = 0;
+ else
+ trans_pcie->n_no_reclaim_cmds = trans_cfg->n_no_reclaim_cmds;
+ if (trans_pcie->n_no_reclaim_cmds)
+ memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds,
+ trans_pcie->n_no_reclaim_cmds * sizeof(u8));
}
static void iwl_trans_pcie_free(struct iwl_trans *trans)
@@ -2322,6 +2329,9 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct iwl_shared *shrd,
pci_write_config_word(pdev, PCI_COMMAND, pci_cmd);
}
+ /* Initialize the wait queue for commands */
+ init_waitqueue_head(&trans->wait_command_queue);
+
return trans;
out_pci_release_regions:
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index 7d1990c7f658..0c81cbaa8088 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -274,6 +274,8 @@ static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r)
return p;
}
+#define MAX_NO_RECLAIM_CMDS 6
+
/**
* struct iwl_trans_config - transport configuration
*
@@ -281,10 +283,17 @@ static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r)
* Must be set before any other call.
* @cmd_queue: the index of the command queue.
* Must be set before start_fw.
+ * @no_reclaim_cmds: Some devices erroneously don't set the
+ * SEQ_RX_FRAME bit on some notifications, this is the
+ * list of such notifications to filter. Max length is
+ * %MAX_NO_RECLAIM_CMDS.
+ * @n_no_reclaim_cmds: # of commands in list
*/
struct iwl_trans_config {
struct iwl_op_mode *op_mode;
u8 cmd_queue;
+ const u8 *no_reclaim_cmds;
+ int n_no_reclaim_cmds;
};
/**
@@ -404,6 +413,7 @@ enum iwl_trans_state {
* @hw_id_str: a string with info about HW ID. Set during transport allocation.
* @nvm_device_type: indicates OTP or eeprom
* @pm_support: set to true in start_hw if link pm is supported
+ * @wait_command_queue: the wait_queue for SYNC host commands
*/
struct iwl_trans {
const struct iwl_trans_ops *ops;
@@ -420,6 +430,8 @@ struct iwl_trans {
int nvm_device_type;
bool pm_support;
+ wait_queue_head_t wait_command_queue;
+
/* pointer to trans specific struct */
/*Ensure that this pointer will always be aligned to sizeof pointer */
char trans_specific[0] __aligned(sizeof(void *));
@@ -488,8 +500,8 @@ static inline void iwl_trans_wowlan_suspend(struct iwl_trans *trans)
static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
struct iwl_host_cmd *cmd)
{
- if (trans->state != IWL_TRANS_FW_ALIVE)
- IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+ WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
+ "%s bad state = %d", __func__, trans->state);
return trans->ops->send_cmd(trans, cmd);
}
@@ -508,8 +520,8 @@ static inline int iwl_trans_reclaim(struct iwl_trans *trans, int sta_id,
int tid, int txq_id, int ssn,
struct sk_buff_head *skbs)
{
- if (trans->state != IWL_TRANS_FW_ALIVE)
- IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+ WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
+ "%s bad state = %d", __func__, trans->state);
return trans->ops->reclaim(trans, sta_id, tid, txq_id, ssn, skbs);
}
@@ -517,8 +529,8 @@ static inline int iwl_trans_reclaim(struct iwl_trans *trans, int sta_id,
static inline int iwl_trans_tx_agg_disable(struct iwl_trans *trans,
int sta_id, int tid)
{
- if (trans->state != IWL_TRANS_FW_ALIVE)
- IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+ WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
+ "%s bad state = %d", __func__, trans->state);
return trans->ops->tx_agg_disable(trans, sta_id, tid);
}
@@ -526,8 +538,8 @@ static inline int iwl_trans_tx_agg_disable(struct iwl_trans *trans,
static inline int iwl_trans_tx_agg_alloc(struct iwl_trans *trans,
int sta_id, int tid)
{
- if (trans->state != IWL_TRANS_FW_ALIVE)
- IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+ WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
+ "%s bad state = %d", __func__, trans->state);
return trans->ops->tx_agg_alloc(trans, sta_id, tid);
}
@@ -540,8 +552,8 @@ static inline void iwl_trans_tx_agg_setup(struct iwl_trans *trans,
{
might_sleep();
- if (trans->state != IWL_TRANS_FW_ALIVE)
- IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+ WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
+ "%s bad state = %d", __func__, trans->state);
trans->ops->tx_agg_setup(trans, ctx, sta_id, tid, frame_limit, ssn);
}
@@ -553,16 +565,16 @@ static inline void iwl_trans_free(struct iwl_trans *trans)
static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans)
{
- if (trans->state != IWL_TRANS_FW_ALIVE)
- IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+ WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
+ "%s bad state = %d", __func__, trans->state);
return trans->ops->wait_tx_queue_empty(trans);
}
static inline int iwl_trans_check_stuck_queue(struct iwl_trans *trans, int q)
{
- if (trans->state != IWL_TRANS_FW_ALIVE)
- IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+ WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE,
+ "%s bad state = %d", __func__, trans->state);
return trans->ops->check_stuck_queue(trans, q);
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-ucode.c b/drivers/net/wireless/iwlwifi/iwl-ucode.c
index d97cf44b75ba..252828728837 100644
--- a/drivers/net/wireless/iwlwifi/iwl-ucode.c
+++ b/drivers/net/wireless/iwlwifi/iwl-ucode.c
@@ -80,17 +80,10 @@ static struct iwl_wimax_coex_event_entry cu_priorities[COEX_NUM_OF_EVENTS] = {
static inline const struct fw_img *
iwl_get_ucode_image(struct iwl_priv *priv, enum iwl_ucode_type ucode_type)
{
- switch (ucode_type) {
- case IWL_UCODE_INIT:
- return &priv->fw->ucode_init;
- case IWL_UCODE_WOWLAN:
- return &priv->fw->ucode_wowlan;
- case IWL_UCODE_REGULAR:
- return &priv->fw->ucode_rt;
- case IWL_UCODE_NONE:
- break;
- }
- return NULL;
+ if (ucode_type >= IWL_UCODE_TYPE_MAX)
+ return NULL;
+
+ return &priv->fw->img[ucode_type];
}
/*
@@ -342,7 +335,7 @@ static int iwl_alive_notify(struct iwl_priv *priv)
* using sample data 100 bytes apart. If these sample points are good,
* it's a pretty good bet that everything between them is good, too.
*/
-static int iwl_verify_inst_sparse(struct iwl_priv *priv,
+static int iwl_verify_sec_sparse(struct iwl_priv *priv,
const struct fw_desc *fw_desc)
{
__le32 *image = (__le32 *)fw_desc->v_addr;
@@ -357,7 +350,7 @@ static int iwl_verify_inst_sparse(struct iwl_priv *priv,
/* NOTE: Use the debugless read so we don't flood kernel log
* if IWL_DL_IO is set */
iwl_write_direct32(trans(priv), HBUS_TARG_MEM_RADDR,
- i + IWLAGN_RTC_INST_LOWER_BOUND);
+ i + fw_desc->offset);
val = iwl_read32(trans(priv), HBUS_TARG_MEM_RDAT);
if (val != le32_to_cpu(*image))
return -EIO;
@@ -366,7 +359,7 @@ static int iwl_verify_inst_sparse(struct iwl_priv *priv,
return 0;
}
-static void iwl_print_mismatch_inst(struct iwl_priv *priv,
+static void iwl_print_mismatch_sec(struct iwl_priv *priv,
const struct fw_desc *fw_desc)
{
__le32 *image = (__le32 *)fw_desc->v_addr;
@@ -378,7 +371,7 @@ static void iwl_print_mismatch_inst(struct iwl_priv *priv,
IWL_DEBUG_FW(priv, "ucode inst image size is %u\n", len);
iwl_write_direct32(trans(priv), HBUS_TARG_MEM_RADDR,
- IWLAGN_RTC_INST_LOWER_BOUND);
+ fw_desc->offset);
for (offs = 0;
offs < len && errors < 20;
@@ -408,14 +401,14 @@ static int iwl_verify_ucode(struct iwl_priv *priv,
return -EINVAL;
}
- if (!iwl_verify_inst_sparse(priv, &img->code)) {
+ if (!iwl_verify_sec_sparse(priv, &img->sec[IWL_UCODE_SECTION_INST])) {
IWL_DEBUG_FW(priv, "uCode is good in inst SRAM\n");
return 0;
}
IWL_ERR(priv, "UCODE IMAGE IN INSTRUCTION SRAM NOT VALID!!\n");
- iwl_print_mismatch_inst(priv, &img->code);
+ iwl_print_mismatch_sec(priv, &img->sec[IWL_UCODE_SECTION_INST]);
return -EIO;
}
@@ -465,6 +458,8 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
priv->shrd->ucode_type = ucode_type;
fw = iwl_get_ucode_image(priv, ucode_type);
+ priv->ucode_loaded = false;
+
if (!fw)
return -EINVAL;
@@ -519,6 +514,8 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
return ret;
}
+ priv->ucode_loaded = true;
+
return 0;
}
@@ -530,10 +527,10 @@ int iwl_run_init_ucode(struct iwl_priv *priv)
lockdep_assert_held(&priv->mutex);
/* No init ucode required? Curious, but maybe ok */
- if (!priv->fw->ucode_init.code.len)
+ if (!priv->fw->img[IWL_UCODE_INIT].sec[0].len)
return 0;
- if (priv->shrd->ucode_type != IWL_UCODE_NONE)
+ if (priv->init_ucode_run)
return 0;
iwl_init_notification_wait(&priv->notif_wait, &calib_wait,
@@ -555,6 +552,8 @@ int iwl_run_init_ucode(struct iwl_priv *priv)
*/
ret = iwl_wait_notification(&priv->notif_wait, &calib_wait,
UCODE_CALIB_TIMEOUT);
+ if (!ret)
+ priv->init_ucode_run = true;
goto out;
@@ -563,5 +562,7 @@ int iwl_run_init_ucode(struct iwl_priv *priv)
out:
/* Whatever happened, stop the device */
iwl_trans_stop_device(trans(priv));
+ priv->ucode_loaded = false;
+
return ret;
}