Merge tag 'iwlwifi-next-for-kalle-2014-12-30' of https://git.kernel.org/pub/scm/linux...
[cascardo/linux.git] / drivers / net / wireless / iwlwifi / iwl-drv.c
index 77e3178..e766dcd 100644 (file)
@@ -6,6 +6,7 @@
  * GPL LICENSE SUMMARY
  *
  * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -31,6 +32,7 @@
  * BSD LICENSE
  *
  * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -67,6 +69,7 @@
 #include <linux/vmalloc.h>
 
 #include "iwl-drv.h"
+#include "iwl-csr.h"
 #include "iwl-debug.h"
 #include "iwl-trans.h"
 #include "iwl-op-mode.h"
 #include "iwl-config.h"
 #include "iwl-modparams.h"
 
-/* private includes */
-#include "iwl-fw-file.h"
-
 /******************************************************************************
  *
  * module boiler plate
  *
  ******************************************************************************/
 
-/*
- * module name, copyright, version, etc.
- */
 #define DRV_DESCRIPTION        "Intel(R) Wireless WiFi driver for Linux"
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-#define VD "d"
-#else
-#define VD
-#endif
-
-#define DRV_VERSION     IWLWIFI_VERSION VD
-
 MODULE_DESCRIPTION(DRV_DESCRIPTION);
-MODULE_VERSION(DRV_VERSION);
 MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
 MODULE_LICENSE("GPL");
 
@@ -184,6 +171,11 @@ static void iwl_free_fw_img(struct iwl_drv *drv, struct fw_img *img)
 static void iwl_dealloc_ucode(struct iwl_drv *drv)
 {
        int i;
+
+       kfree(drv->fw.dbg_dest_tlv);
+       for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++)
+               kfree(drv->fw.dbg_conf_tlv[i]);
+
        for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
                iwl_free_fw_img(drv, drv->fw.img + i);
 }
@@ -242,6 +234,23 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first)
        snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s%s.ucode",
                 name_pre, tag);
 
+       /*
+        * Starting 8000B - FW name format has changed. This overwrites the
+        * previous name and uses the new format.
+        */
+       if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
+               char rev_step[2] = {
+                       'A' + CSR_HW_REV_STEP(drv->trans->hw_rev), 0
+               };
+
+               /* A-step doesn't have an indication */
+               if (CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP)
+                       rev_step[0] = 0;
+
+               snprintf(drv->firmware_name, sizeof(drv->firmware_name),
+                        "%s%s-%s.ucode", name_pre, rev_step, tag);
+       }
+
        IWL_DEBUG_INFO(drv, "attempting to load firmware %s'%s'\n",
                       (drv->fw_index == UCODE_EXPERIMENTAL_INDEX)
                                ? "EXPERIMENTAL " : "",
@@ -281,6 +290,11 @@ struct iwl_firmware_pieces {
 
        u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
        u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
+
+       /* FW debug data parsed for driver usage */
+       struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+       struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
+       size_t dbg_conf_tlv_len[FW_DBG_MAX];
 };
 
 /*
@@ -554,6 +568,8 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
        char buildstr[25];
        u32 build;
        int num_of_cpus;
+       bool usniffer_images = false;
+       bool usniffer_req = false;
 
        if (len < sizeof(*ucode)) {
                IWL_ERR(drv, "uCode has invalid length: %zd\n", len);
@@ -787,19 +803,16 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                        iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR,
                                            tlv_len);
                        drv->fw.mvm_fw = true;
-                       drv->fw.img[IWL_UCODE_REGULAR].is_secure = true;
                        break;
                case IWL_UCODE_TLV_SECURE_SEC_INIT:
                        iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT,
                                            tlv_len);
                        drv->fw.mvm_fw = true;
-                       drv->fw.img[IWL_UCODE_INIT].is_secure = true;
                        break;
                case IWL_UCODE_TLV_SECURE_SEC_WOWLAN:
                        iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN,
                                            tlv_len);
                        drv->fw.mvm_fw = true;
-                       drv->fw.img[IWL_UCODE_WOWLAN].is_secure = true;
                        break;
                case IWL_UCODE_TLV_NUM_OF_CPU:
                        if (tlv_len != sizeof(u32))
@@ -829,12 +842,85 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                        capa->n_scan_channels =
                                le32_to_cpup((__le32 *)tlv_data);
                        break;
+               case IWL_UCODE_TLV_FW_DBG_DEST: {
+                       struct iwl_fw_dbg_dest_tlv *dest = (void *)tlv_data;
+
+                       if (pieces->dbg_dest_tlv) {
+                               IWL_ERR(drv,
+                                       "dbg destination ignored, already exists\n");
+                               break;
+                       }
+
+                       pieces->dbg_dest_tlv = dest;
+                       IWL_INFO(drv, "Found debug destination: %s\n",
+                                get_fw_dbg_mode_string(dest->monitor_mode));
+
+                       drv->fw.dbg_dest_reg_num =
+                               tlv_len - offsetof(struct iwl_fw_dbg_dest_tlv,
+                                                  reg_ops);
+                       drv->fw.dbg_dest_reg_num /=
+                               sizeof(drv->fw.dbg_dest_tlv->reg_ops[0]);
+
+                       break;
+                       }
+               case IWL_UCODE_TLV_FW_DBG_CONF: {
+                       struct iwl_fw_dbg_conf_tlv *conf = (void *)tlv_data;
+
+                       if (!pieces->dbg_dest_tlv) {
+                               IWL_ERR(drv,
+                                       "Ignore dbg config %d - no destination configured\n",
+                                       conf->id);
+                               break;
+                       }
+
+                       if (conf->id >= ARRAY_SIZE(drv->fw.dbg_conf_tlv)) {
+                               IWL_ERR(drv,
+                                       "Skip unknown configuration: %d\n",
+                                       conf->id);
+                               break;
+                       }
+
+                       if (pieces->dbg_conf_tlv[conf->id]) {
+                               IWL_ERR(drv,
+                                       "Ignore duplicate dbg config %d\n",
+                                       conf->id);
+                               break;
+                       }
+
+                       if (conf->usniffer)
+                               usniffer_req = true;
+
+                       IWL_INFO(drv, "Found debug configuration: %d\n",
+                                conf->id);
+
+                       pieces->dbg_conf_tlv[conf->id] = conf;
+                       pieces->dbg_conf_tlv_len[conf->id] = tlv_len;
+                       break;
+                       }
+               case IWL_UCODE_TLV_SEC_RT_USNIFFER:
+                       usniffer_images = true;
+                       iwl_store_ucode_sec(pieces, tlv_data,
+                                           IWL_UCODE_REGULAR_USNIFFER,
+                                           tlv_len);
+                       break;
+               case IWL_UCODE_TLV_SDIO_ADMA_ADDR:
+                       if (tlv_len != sizeof(u32))
+                               goto invalid_tlv_len;
+                       drv->fw.sdio_adma_addr =
+                               le32_to_cpup((__le32 *)tlv_data);
+                       break;
                default:
                        IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
                        break;
                }
        }
 
+       if (usniffer_req && !usniffer_images) {
+               IWL_ERR(drv,
+                       "user selected to work with usniffer but usniffer image isn't available in ucode package\n");
+               return -EINVAL;
+       }
+
        if (len) {
                IWL_ERR(drv, "invalid TLV after parsing: %zd\n", len);
                iwl_print_hex_dump(drv, IWL_DL_FW, (u8 *)data, len);
@@ -972,7 +1058,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
        struct iwl_ucode_header *ucode;
        struct iwlwifi_opmode_table *op;
        int err;
-       struct iwl_firmware_pieces pieces;
+       struct iwl_firmware_pieces *pieces;
        const unsigned int api_max = drv->cfg->ucode_api_max;
        unsigned int api_ok = drv->cfg->ucode_api_ok;
        const unsigned int api_min = drv->cfg->ucode_api_min;
@@ -988,7 +1074,9 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
        if (!api_ok)
                api_ok = api_max;
 
-       memset(&pieces, 0, sizeof(pieces));
+       pieces = kzalloc(sizeof(*pieces), GFP_KERNEL);
+       if (!pieces)
+               return;
 
        if (!ucode_raw) {
                if (drv->fw_index <= api_ok)
@@ -1011,10 +1099,10 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
        ucode = (struct iwl_ucode_header *)ucode_raw->data;
 
        if (ucode->ver)
-               err = iwl_parse_v1_v2_firmware(drv, ucode_raw, &pieces);
+               err = iwl_parse_v1_v2_firmware(drv, ucode_raw, pieces);
        else
-               err = iwl_parse_tlv_firmware(drv, ucode_raw, &pieces,
-                                          &fw->ucode_capa);
+               err = iwl_parse_tlv_firmware(drv, ucode_raw, pieces,
+                                            &fw->ucode_capa);
 
        if (err)
                goto try_again;
@@ -1054,7 +1142,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
         * In mvm uCode there is no difference between data and instructions
         * sections.
         */
-       if (!fw->mvm_fw && validate_sec_sizes(drv, &pieces, drv->cfg))
+       if (!fw->mvm_fw && validate_sec_sizes(drv, pieces, drv->cfg))
                goto try_again;
 
        /* Allocate ucode buffers for card's bus-master loading ... */
@@ -1063,9 +1151,33 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
         * 1) unmodified from disk
         * 2) backup cache for save/restore during power-downs */
        for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
-               if (iwl_alloc_ucode(drv, &pieces, i))
+               if (iwl_alloc_ucode(drv, pieces, i))
                        goto out_free_fw;
 
+       if (pieces->dbg_dest_tlv) {
+               drv->fw.dbg_dest_tlv =
+                       kmemdup(pieces->dbg_dest_tlv,
+                               sizeof(*pieces->dbg_dest_tlv) +
+                               sizeof(pieces->dbg_dest_tlv->reg_ops[0]) *
+                               drv->fw.dbg_dest_reg_num, GFP_KERNEL);
+
+               if (!drv->fw.dbg_dest_tlv)
+                       goto out_free_fw;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++) {
+               if (pieces->dbg_conf_tlv[i]) {
+                       drv->fw.dbg_conf_tlv_len[i] =
+                               pieces->dbg_conf_tlv_len[i];
+                       drv->fw.dbg_conf_tlv[i] =
+                               kmemdup(pieces->dbg_conf_tlv[i],
+                                       drv->fw.dbg_conf_tlv_len[i],
+                                       GFP_KERNEL);
+                       if (!drv->fw.dbg_conf_tlv[i])
+                               goto out_free_fw;
+               }
+       }
+
        /* Now that we can no longer fail, copy information */
 
        /*
@@ -1073,20 +1185,20 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
         * for each event, which is of mode 1 (including timestamp) for all
         * new microcodes that include this information.
         */
-       fw->init_evtlog_ptr = pieces.init_evtlog_ptr;
-       if (pieces.init_evtlog_size)
-               fw->init_evtlog_size = (pieces.init_evtlog_size - 16)/12;
+       fw->init_evtlog_ptr = pieces->init_evtlog_ptr;
+       if (pieces->init_evtlog_size)
+               fw->init_evtlog_size = (pieces->init_evtlog_size - 16)/12;
        else
                fw->init_evtlog_size =
                        drv->cfg->base_params->max_event_log_size;
-       fw->init_errlog_ptr = pieces.init_errlog_ptr;
-       fw->inst_evtlog_ptr = pieces.inst_evtlog_ptr;
-       if (pieces.inst_evtlog_size)
-               fw->inst_evtlog_size = (pieces.inst_evtlog_size - 16)/12;
+       fw->init_errlog_ptr = pieces->init_errlog_ptr;
+       fw->inst_evtlog_ptr = pieces->inst_evtlog_ptr;
+       if (pieces->inst_evtlog_size)
+               fw->inst_evtlog_size = (pieces->inst_evtlog_size - 16)/12;
        else
                fw->inst_evtlog_size =
                        drv->cfg->base_params->max_event_log_size;
-       fw->inst_errlog_ptr = pieces.inst_errlog_ptr;
+       fw->inst_errlog_ptr = pieces->inst_errlog_ptr;
 
        /*
         * figure out the offset of chain noise reset and gain commands
@@ -1152,6 +1264,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
        release_firmware(ucode_raw);
        if (iwl_request_firmware(drv, false))
                goto out_unbind;
+       kfree(pieces);
        return;
 
  out_free_fw:
@@ -1159,6 +1272,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
        iwl_dealloc_ucode(drv);
        release_firmware(ucode_raw);
  out_unbind:
+       kfree(pieces);
        complete(&drv->request_firmware_complete);
        device_release_driver(drv->trans->dev);
 }
@@ -1254,7 +1368,10 @@ struct iwl_mod_params iwlwifi_mod_params = {
        .bt_coex_active = true,
        .power_level = IWL_POWER_INDEX_1,
        .wd_disable = true,
-       .uapsd_disable = false,
+       .d0i3_disable = true,
+#ifndef CONFIG_IWLWIFI_UAPSD
+       .uapsd_disable = true,
+#endif /* CONFIG_IWLWIFI_UAPSD */
        /* the rest are 0 by default */
 };
 IWL_EXPORT_SYMBOL(iwlwifi_mod_params);
@@ -1314,7 +1431,7 @@ static int __init iwl_drv_init(void)
        for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++)
                INIT_LIST_HEAD(&iwlwifi_opmode_table[i].drv);
 
-       pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n");
+       pr_info(DRV_DESCRIPTION "\n");
        pr_info(DRV_COPYRIGHT "\n");
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -1359,7 +1476,7 @@ MODULE_PARM_DESC(fw_restart, "restart firmware in case of error (default true)")
 module_param_named(antenna_coupling, iwlwifi_mod_params.ant_coupling,
                   int, S_IRUGO);
 MODULE_PARM_DESC(antenna_coupling,
-                "specify antenna coupling in dB (defualt: 0 dB)");
+                "specify antenna coupling in dB (default: 0 dB)");
 
 module_param_named(wd_disable, iwlwifi_mod_params.wd_disable, int, S_IRUGO);
 MODULE_PARM_DESC(wd_disable,
@@ -1368,9 +1485,17 @@ MODULE_PARM_DESC(wd_disable,
 module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO);
 MODULE_PARM_DESC(nvm_file, "NVM file name");
 
+module_param_named(d0i3_disable, iwlwifi_mod_params.d0i3_disable,
+                  bool, S_IRUGO);
+MODULE_PARM_DESC(d0i3_disable, "disable d0i3 functionality (default: Y)");
+
 module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable,
                   bool, S_IRUGO);
+#ifdef CONFIG_IWLWIFI_UAPSD
 MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: N)");
+#else
+MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: Y)");
+#endif
 
 /*
  * set bt_coex_active to true, uCode will do kill/defer