blob: a7bd4b5a4b39ba5fdbba2d176bfa47a6ec19671a [file] [log] [blame]
/******************************************************************************
*
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2015 - 2017 Intel Deutschland GmbH
* Copyright (C) 2018 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
#include "fw/dbg.h"
#include "fw/img.h"
#include "fw/testmode.h"
#include "iwl-csr.h"
#include "iwl-dnt-cfg.h"
#include "iwl-op-mode.h"
#include "iwl-trans.h"
#include "xvt.h"
#define XVT_UCODE_ALIVE_TIMEOUT (HZ * CPTCFG_IWL_TIMEOUT_FACTOR)
struct iwl_xvt_alive_data {
bool valid;
uint32_t scd_base_addr;
};
static int iwl_xvt_send_dqa_cmd(struct iwl_xvt* xvt) {
struct iwl_dqa_enable_cmd dqa_cmd = {
.cmd_queue = cpu_to_le32(IWL_MVM_DQA_CMD_QUEUE),
};
uint32_t cmd_id = iwl_cmd_id(DQA_ENABLE_CMD, DATA_PATH_GROUP, 0);
int ret;
ret = iwl_xvt_send_cmd_pdu(xvt, cmd_id, 0, sizeof(dqa_cmd), &dqa_cmd);
if (ret) {
IWL_ERR(xvt, "Failed to send DQA enabling command: %d\n", ret);
} else {
IWL_DEBUG_FW(xvt, "Working in DQA mode\n");
}
return ret;
}
static bool iwl_alive_fn(struct iwl_notif_wait_data* notif_wait, struct iwl_rx_packet* pkt,
void* data) {
struct iwl_xvt* xvt = container_of(notif_wait, struct iwl_xvt, notif_wait);
struct iwl_xvt_alive_data* alive_data = data;
struct xvt_alive_resp_ver2* palive2;
struct mvm_alive_resp_v3* palive3;
struct mvm_alive_resp* palive4;
struct iwl_lmac_alive *lmac1, *lmac2;
struct iwl_umac_alive* umac;
uint32_t rx_packet_payload_size = iwl_rx_packet_payload_len(pkt);
uint16_t status, flags;
xvt->support_umac_log = false;
if (rx_packet_payload_size == sizeof(*palive2)) {
palive2 = (void*)pkt->data;
xvt->error_event_table[0] = le32_to_cpu(palive2->error_event_table_ptr);
alive_data->scd_base_addr = le32_to_cpu(palive2->scd_base_ptr);
alive_data->valid = le16_to_cpu(palive2->status) == IWL_ALIVE_STATUS_OK;
iwl_tm_set_fw_ver(xvt->trans, palive2->ucode_major, palive2->ucode_minor);
xvt->umac_error_event_table = le32_to_cpu(palive2->error_info_addr);
if (xvt->umac_error_event_table) {
xvt->support_umac_log = true;
}
IWL_DEBUG_FW(xvt,
"Alive VER2 ucode status 0x%04x revision 0x%01X "
"0x%01X flags 0x%01X\n",
le16_to_cpu(palive2->status), palive2->ver_type, palive2->ver_subtype,
palive2->flags);
IWL_DEBUG_FW(xvt, "UMAC version: Major - 0x%x, Minor - 0x%x\n", palive2->umac_major,
palive2->umac_minor);
} else {
if (rx_packet_payload_size == sizeof(*palive3)) {
palive3 = (void*)pkt->data;
status = le16_to_cpu(palive3->status);
flags = le16_to_cpu(palive3->flags);
lmac1 = &palive3->lmac_data;
umac = &palive3->umac_data;
IWL_DEBUG_FW(xvt, "Alive VER3\n");
} else if (rx_packet_payload_size == sizeof(*palive4)) {
palive4 = (void*)pkt->data;
status = le16_to_cpu(palive4->status);
flags = le16_to_cpu(palive4->flags);
lmac1 = &palive4->lmac_data[0];
lmac2 = &palive4->lmac_data[1];
umac = &palive4->umac_data;
xvt->error_event_table[1] = le32_to_cpu(lmac2->error_event_table_ptr);
IWL_DEBUG_FW(xvt, "Alive VER4 CDB\n");
} else {
IWL_ERR(xvt, "unrecognized alive notificatio\n");
return false;
}
alive_data->valid = status == IWL_ALIVE_STATUS_OK;
xvt->error_event_table[0] = le32_to_cpu(lmac1->error_event_table_ptr);
alive_data->scd_base_addr = le32_to_cpu(lmac1->scd_base_ptr);
iwl_tm_set_fw_ver(xvt->trans, le32_to_cpu(lmac1->ucode_major), le32_to_cpu(lmac1->ucode_minor));
xvt->umac_error_event_table = le32_to_cpu(umac->error_info_addr);
if (xvt->umac_error_event_table) {
xvt->support_umac_log = true;
}
IWL_DEBUG_FW(xvt, "status 0x%04x rev 0x%01X 0x%01X flags 0x%01X\n", status, lmac1->ver_type,
lmac1->ver_subtype, flags);
IWL_DEBUG_FW(xvt, "UMAC version: Major - 0x%x, Minor - 0x%x\n", umac->umac_major,
umac->umac_minor);
}
return true;
}
static int iwl_xvt_load_ucode_wait_alive(struct iwl_xvt* xvt, enum iwl_ucode_type ucode_type) {
struct iwl_notification_wait alive_wait;
struct iwl_xvt_alive_data alive_data;
const struct fw_img* fw;
int ret;
enum iwl_ucode_type old_type = xvt->fwrt.cur_fw_img;
static const uint16_t alive_cmd[] = {MVM_ALIVE};
struct iwl_scd_txq_cfg_cmd cmd = {
.scd_queue = IWL_XVT_DEFAULT_TX_QUEUE,
.action = SCD_CFG_ENABLE_QUEUE,
.window = IWL_FRAME_LIMIT,
.sta_id = IWL_XVT_TX_STA_ID_DEFAULT,
.ssn = 0,
.tx_fifo = IWL_XVT_DEFAULT_TX_FIFO,
.aggregate = false,
.tid = IWL_MAX_TID_COUNT,
};
iwl_fw_set_current_image(&xvt->fwrt, ucode_type);
fw = iwl_get_ucode_image(xvt->fw, ucode_type);
if (!fw) {
return -EINVAL;
}
iwl_init_notification_wait(&xvt->notif_wait, &alive_wait, alive_cmd, ARRAY_SIZE(alive_cmd),
iwl_alive_fn, &alive_data);
ret =
iwl_trans_start_fw_dbg(xvt->trans, fw, ucode_type == IWL_UCODE_INIT,
(xvt->sw_stack_cfg.fw_dbg_flags & ~IWL_XVT_DBG_FLAGS_NO_DEFAULT_TXQ));
if (ret) {
iwl_fw_set_current_image(&xvt->fwrt, old_type);
iwl_remove_notification(&xvt->notif_wait, &alive_wait);
return ret;
}
/*
* Some things may run in the background now, but we
* just wait for the ALIVE notification here.
*/
ret = iwl_wait_notification(&xvt->notif_wait, &alive_wait, XVT_UCODE_ALIVE_TIMEOUT);
if (ret) {
iwl_fw_set_current_image(&xvt->fwrt, old_type);
return ret;
}
if (!alive_data.valid) {
IWL_ERR(xvt, "Loaded ucode is not valid!\n");
iwl_fw_set_current_image(&xvt->fwrt, old_type);
return -EIO;
}
/* fresh firmware was loaded */
xvt->fw_error = false;
iwl_trans_fw_alive(xvt->trans, alive_data.scd_base_addr);
ret = iwl_init_paging(&xvt->fwrt, ucode_type);
if (ret) {
return ret;
}
if (ucode_type == IWL_UCODE_REGULAR) {
ret = iwl_xvt_send_dqa_cmd(xvt);
if (ret) {
return ret;
}
}
/*
* Starting from 22000 tx queue allocation must be done after add
* station, so it is not part of the init flow.
*/
if (!iwl_xvt_is_unified_fw(xvt) && iwl_xvt_has_default_txq(xvt)) {
iwl_trans_txq_enable_cfg(xvt->trans, IWL_XVT_DEFAULT_TX_QUEUE, 0, NULL, 0);
WARN(iwl_xvt_send_cmd_pdu(xvt, SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd),
"Failed to configure queue %d on FIFO %d\n", IWL_XVT_DEFAULT_TX_QUEUE,
IWL_XVT_DEFAULT_TX_FIFO);
xvt->tx_meta_data[XVT_LMAC_0_ID].queue = IWL_XVT_DEFAULT_TX_QUEUE;
}
xvt->fw_running = true;
return 0;
}
static int iwl_xvt_send_extended_config(struct iwl_xvt* xvt) {
/*
* TODO: once WRT will be implemented in xVT, IWL_INIT_DEBUG_CFG
* flag will not always be set
*/
struct iwl_init_extended_cfg_cmd ext_cfg = {
.init_flags = cpu_to_le32(BIT(IWL_INIT_NVM) | BIT(IWL_INIT_DEBUG_CFG)),
};
if (xvt->sw_stack_cfg.load_mask & IWL_XVT_LOAD_MASK_RUNTIME) {
ext_cfg.init_flags |= cpu_to_le32(BIT(IWL_INIT_PHY));
}
return iwl_xvt_send_cmd_pdu(xvt, WIDE_ID(SYSTEM_GROUP, INIT_EXTENDED_CFG_CMD), 0, sizeof(ext_cfg),
&ext_cfg);
}
int iwl_xvt_run_fw(struct iwl_xvt* xvt, uint32_t ucode_type, bool cont_run) {
int ret;
if (ucode_type >= IWL_UCODE_TYPE_MAX) {
return -EINVAL;
}
iwl_assert_lock_held(&xvt->mutex);
if (xvt->state != IWL_XVT_STATE_UNINITIALIZED) {
if (xvt->fw_running) {
xvt->fw_running = false;
if (xvt->fwrt.cur_fw_img == IWL_UCODE_REGULAR) {
iwl_xvt_txq_disable(xvt);
}
}
_iwl_trans_stop_device(xvt->trans, !cont_run);
}
if (cont_run) {
ret = _iwl_trans_start_hw(xvt->trans, false);
} else {
ret = iwl_trans_start_hw(xvt->trans);
}
if (ret) {
IWL_ERR(xvt, "Failed to start HW\n");
return ret;
}
iwl_trans_set_bits_mask(xvt->trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_MAC_SI,
CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
/* Will also start the device */
ret = iwl_xvt_load_ucode_wait_alive(xvt, ucode_type);
if (ret) {
IWL_ERR(xvt, "Failed to start ucode: %d\n", ret);
iwl_trans_stop_device(xvt->trans);
}
if (iwl_xvt_is_unified_fw(xvt)) {
ret = iwl_xvt_send_extended_config(xvt);
if (ret) {
IWL_ERR(xvt, "Failed to send extended_config: %d\n", ret);
iwl_trans_stop_device(xvt->trans);
return ret;
}
}
iwl_dnt_start(xvt->trans);
xvt->fwrt.dump.conf = FW_DBG_INVALID;
/* if we have a destination, assume EARLY START */
if (xvt->fw->dbg.dest_tlv) {
xvt->fwrt.dump.conf = FW_DBG_START_FROM_ALIVE;
}
iwl_fw_start_dbg_conf(&xvt->fwrt, FW_DBG_START_FROM_ALIVE);
return ret;
}