Merge branch 'iwlwifi-fixes' into iwlwifi-next
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / iwlwifi / mvm / fw.c
index d0fa6e9ed59098cb52e05102e72afbffe3719fc8..6cf7d9837ca54e938500302cd013a8a4a396be5d 100644 (file)
@@ -70,6 +70,7 @@
 #include "iwl-debug.h"
 #include "iwl-csr.h" /* for iwl_mvm_rx_card_state_notif */
 #include "iwl-io.h" /* for iwl_mvm_rx_card_state_notif */
+#include "iwl-prph.h"
 #include "iwl-eeprom-parse.h"
 
 #include "mvm.h"
@@ -111,25 +112,27 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
        struct iwl_mvm *mvm =
                container_of(notif_wait, struct iwl_mvm, notif_wait);
        struct iwl_mvm_alive_data *alive_data = data;
-       struct mvm_alive_resp *palive;
+       struct mvm_alive_resp_ver1 *palive1;
        struct mvm_alive_resp_ver2 *palive2;
+       struct mvm_alive_resp *palive;
 
-       if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) {
-               palive = (void *)pkt->data;
+       if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive1)) {
+               palive1 = (void *)pkt->data;
 
                mvm->support_umac_log = false;
                mvm->error_event_table =
-                       le32_to_cpu(palive->error_event_table_ptr);
-               mvm->log_event_table = le32_to_cpu(palive->log_event_table_ptr);
-               alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
+                       le32_to_cpu(palive1->error_event_table_ptr);
+               mvm->log_event_table =
+                       le32_to_cpu(palive1->log_event_table_ptr);
+               alive_data->scd_base_addr = le32_to_cpu(palive1->scd_base_ptr);
 
-               alive_data->valid = le16_to_cpu(palive->status) ==
+               alive_data->valid = le16_to_cpu(palive1->status) ==
                                    IWL_ALIVE_STATUS_OK;
                IWL_DEBUG_FW(mvm,
                             "Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
-                            le16_to_cpu(palive->status), palive->ver_type,
-                            palive->ver_subtype, palive->flags);
-       } else {
+                            le16_to_cpu(palive1->status), palive1->ver_type,
+                            palive1->ver_subtype, palive1->flags);
+       } else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive2)) {
                palive2 = (void *)pkt->data;
 
                mvm->error_event_table =
@@ -155,6 +158,33 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
                IWL_DEBUG_FW(mvm,
                             "UMAC version: Major - 0x%x, Minor - 0x%x\n",
                             palive2->umac_major, palive2->umac_minor);
+       } else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) {
+               palive = (void *)pkt->data;
+
+               mvm->error_event_table =
+                       le32_to_cpu(palive->error_event_table_ptr);
+               mvm->log_event_table =
+                       le32_to_cpu(palive->log_event_table_ptr);
+               alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
+               mvm->umac_error_event_table =
+                       le32_to_cpu(palive->error_info_addr);
+               mvm->sf_space.addr = le32_to_cpu(palive->st_fwrd_addr);
+               mvm->sf_space.size = le32_to_cpu(palive->st_fwrd_size);
+
+               alive_data->valid = le16_to_cpu(palive->status) ==
+                                   IWL_ALIVE_STATUS_OK;
+               if (mvm->umac_error_event_table)
+                       mvm->support_umac_log = true;
+
+               IWL_DEBUG_FW(mvm,
+                            "Alive VER3 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
+                            le16_to_cpu(palive->status), palive->ver_type,
+                            palive->ver_subtype, palive->flags);
+
+               IWL_DEBUG_FW(mvm,
+                            "UMAC version: Major - 0x%x, Minor - 0x%x\n",
+                            le32_to_cpu(palive->umac_major),
+                            le32_to_cpu(palive->umac_minor));
        }
 
        return true;
@@ -187,8 +217,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
        struct iwl_sf_region st_fwrd_space;
 
        if (ucode_type == IWL_UCODE_REGULAR &&
-           iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_CUSTOM) &&
-           iwl_fw_dbg_conf_enabled(mvm->fw, FW_DBG_CUSTOM))
+           iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE))
                fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER);
        else
                fw = iwl_get_ucode_image(mvm, ucode_type);
@@ -269,7 +298,7 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)
        enum iwl_ucode_type ucode_type = mvm->cur_ucode;
 
        /* Set parameters */
-       phy_cfg_cmd.phy_cfg = cpu_to_le32(mvm->fw->phy_config);
+       phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_get_phy_config(mvm));
        phy_cfg_cmd.calib_control.event_trigger =
                mvm->fw->default_calib[ucode_type].event_trigger;
        phy_cfg_cmd.calib_control.flow_trigger =
@@ -346,7 +375,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
        mvm->calibrating = true;
 
        /* Send TX valid antennas before triggering calibrations */
-       ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
+       ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
        if (ret)
                goto error;
 
@@ -399,8 +428,131 @@ out:
        return ret;
 }
 
-static int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm,
-                                    enum iwl_fw_dbg_conf conf_id)
+static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm)
+{
+       struct iwl_host_cmd cmd = {
+               .id = SHARED_MEM_CFG,
+               .flags = CMD_WANT_SKB,
+               .data = { NULL, },
+               .len = { 0, },
+       };
+       struct iwl_rx_packet *pkt;
+       struct iwl_shared_mem_cfg *mem_cfg;
+       u32 i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (WARN_ON(iwl_mvm_send_cmd(mvm, &cmd)))
+               return;
+
+       pkt = cmd.resp_pkt;
+       if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
+               IWL_ERR(mvm, "Bad return from SHARED_MEM_CFG (0x%08X)\n",
+                       pkt->hdr.flags);
+               goto exit;
+       }
+
+       mem_cfg = (void *)pkt->data;
+
+       mvm->shared_mem_cfg.shared_mem_addr =
+               le32_to_cpu(mem_cfg->shared_mem_addr);
+       mvm->shared_mem_cfg.shared_mem_size =
+               le32_to_cpu(mem_cfg->shared_mem_size);
+       mvm->shared_mem_cfg.sample_buff_addr =
+               le32_to_cpu(mem_cfg->sample_buff_addr);
+       mvm->shared_mem_cfg.sample_buff_size =
+               le32_to_cpu(mem_cfg->sample_buff_size);
+       mvm->shared_mem_cfg.txfifo_addr = le32_to_cpu(mem_cfg->txfifo_addr);
+       for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++)
+               mvm->shared_mem_cfg.txfifo_size[i] =
+                       le32_to_cpu(mem_cfg->txfifo_size[i]);
+       for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++)
+               mvm->shared_mem_cfg.rxfifo_size[i] =
+                       le32_to_cpu(mem_cfg->rxfifo_size[i]);
+       mvm->shared_mem_cfg.page_buff_addr =
+               le32_to_cpu(mem_cfg->page_buff_addr);
+       mvm->shared_mem_cfg.page_buff_size =
+               le32_to_cpu(mem_cfg->page_buff_size);
+       IWL_DEBUG_INFO(mvm, "SHARED MEM CFG: got memory offsets/sizes\n");
+
+exit:
+       iwl_free_resp(&cmd);
+}
+
+int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
+                               struct iwl_mvm_dump_desc *desc,
+                               unsigned int delay)
+{
+       if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status))
+               return -EBUSY;
+
+       if (WARN_ON(mvm->fw_dump_desc))
+               iwl_mvm_free_fw_dump_desc(mvm);
+
+       IWL_WARN(mvm, "Collecting data: trigger %d fired.\n",
+                le32_to_cpu(desc->trig_desc.type));
+
+       mvm->fw_dump_desc = desc;
+
+       /* stop recording */
+       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+               iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
+       } else {
+               iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 0);
+               /* wait before we collect the data till the DBGC stop */
+               udelay(100);
+       }
+
+       queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay);
+
+       return 0;
+}
+
+int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
+                          const char *str, size_t len, unsigned int delay)
+{
+       struct iwl_mvm_dump_desc *desc;
+
+       desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC);
+       if (!desc)
+               return -ENOMEM;
+
+       desc->len = len;
+       desc->trig_desc.type = cpu_to_le32(trig);
+       memcpy(desc->trig_desc.data, str, len);
+
+       return iwl_mvm_fw_dbg_collect_desc(mvm, desc, delay);
+}
+
+int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
+                               struct iwl_fw_dbg_trigger_tlv *trigger,
+                               const char *str, size_t len)
+{
+       unsigned int delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay));
+       u16 occurrences = le16_to_cpu(trigger->occurrences);
+       int ret;
+
+       if (!occurrences)
+               return 0;
+
+       ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), str,
+                                    len, delay);
+       if (ret)
+               return ret;
+
+       trigger->occurrences = cpu_to_le16(occurrences - 1);
+       return 0;
+}
+
+static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm)
+{
+       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
+               iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
+       else
+               iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1);
+}
+
+int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id)
 {
        u8 *ptr;
        int ret;
@@ -410,6 +562,14 @@ static int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm,
                      "Invalid configuration %d\n", conf_id))
                return -EINVAL;
 
+       /* EARLY START - firmware's configuration is hard coded */
+       if ((!mvm->fw->dbg_conf_tlv[conf_id] ||
+            !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) &&
+           conf_id == FW_DBG_START_FROM_ALIVE) {
+               iwl_mvm_restart_early_start(mvm);
+               return 0;
+       }
+
        if (!mvm->fw->dbg_conf_tlv[conf_id])
                return -EINVAL;
 
@@ -435,6 +595,35 @@ static int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm,
        return ret;
 }
 
+static int iwl_mvm_config_ltr_v1(struct iwl_mvm *mvm)
+{
+       struct iwl_ltr_config_cmd_v1 cmd_v1 = {
+               .flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE),
+       };
+
+       if (!mvm->trans->ltr_enabled)
+               return 0;
+
+       return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0,
+                                   sizeof(cmd_v1), &cmd_v1);
+}
+
+static int iwl_mvm_config_ltr(struct iwl_mvm *mvm)
+{
+       struct iwl_ltr_config_cmd cmd = {
+               .flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE),
+       };
+
+       if (!mvm->trans->ltr_enabled)
+               return 0;
+
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_HDC_PHASE_0))
+               return iwl_mvm_config_ltr_v1(mvm);
+
+       return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0,
+                                   sizeof(cmd), &cmd);
+}
+
 int iwl_mvm_up(struct iwl_mvm *mvm)
 {
        int ret, i;
@@ -482,14 +671,20 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
                goto error;
        }
 
+       if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 10)
+               iwl_mvm_get_shared_mem_conf(mvm);
+
        ret = iwl_mvm_sf_update(mvm, NULL, false);
        if (ret)
                IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
 
        mvm->fw_dbg_conf = FW_DBG_INVALID;
-       iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_CUSTOM);
+       /* if we have a destination, assume EARLY START */
+       if (mvm->fw->dbg_dest_tlv)
+               mvm->fw_dbg_conf = FW_DBG_START_FROM_ALIVE;
+       iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_START_FROM_ALIVE);
 
-       ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
+       ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
        if (ret)
                goto error;
 
@@ -538,19 +733,22 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        /* Initialize tx backoffs to the minimal possible */
        iwl_mvm_tt_tx_backoff(mvm, 0);
 
-       if (mvm->trans->ltr_enabled) {
-               struct iwl_ltr_config_cmd cmd = {
-                       .flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE),
-               };
-
-               WARN_ON(iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0,
-                                            sizeof(cmd), &cmd));
-       }
+       WARN_ON(iwl_mvm_config_ltr(mvm));
 
        ret = iwl_mvm_power_update_device(mvm);
        if (ret)
                goto error;
 
+       /*
+        * RTNL is not taken during Ct-kill, but we don't need to scan/Tx
+        * anyway, so don't init MCC.
+        */
+       if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) {
+               ret = iwl_mvm_init_mcc(mvm);
+               if (ret)
+                       goto error;
+       }
+
        if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
                ret = iwl_mvm_config_scan(mvm);
                if (ret)
@@ -584,7 +782,7 @@ int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm)
                goto error;
        }
 
-       ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
+       ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
        if (ret)
                goto error;