blob: 701eda6a9d8deb3d77c9c2c997bd295580952359 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2005-2014, 2018-2024 Intel Corporation
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
#include "iwl-trans.h"
#include "iwl-op-mode.h"
#include "fw/img.h"
#include "iwl-csr.h"
#include "xvt.h"
#include "iwl-dnt-cfg.h"
#include "fw/dbg.h"
#include "fw/testmode.h"
#include "fw/api/power.h"
#include "fw/pnvm.h"
#define XVT_UCODE_ALIVE_TIMEOUT (HZ * CPTCFG_IWL_TIMEOUT_FACTOR)
struct iwl_xvt_alive_data {
bool valid;
u32 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),
};
u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, DQA_ENABLE_CMD);
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 iwl_alive_ntf_v3 *palive3;
struct iwl_alive_ntf_v4 *palive4;
struct iwl_alive_ntf_v5 *palive5;
struct iwl_lmac_alive *lmac1, *lmac2;
struct iwl_umac_alive *umac;
u32 rx_packet_payload_size = iwl_rx_packet_payload_len(pkt);
u16 status, flags;
u32 lmac_error_event_table, umac_error_event_table;
u32 version = iwl_fw_lookup_notif_ver(xvt->fw, LEGACY_GROUP,
UCODE_ALIVE_NTFY, 0);
if (rx_packet_payload_size == sizeof(*palive2)) {
palive2 = (void *)pkt->data;
lmac_error_event_table =
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);
umac_error_event_table =
le32_to_cpu(palive2->error_info_addr);
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)) {
__le32 lmac2_err_ptr;
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;
lmac2_err_ptr = lmac2->dbg_ptrs.error_event_table_ptr;
xvt->trans->dbg.lmac_error_event_table[1] =
le32_to_cpu(lmac2_err_ptr);
IWL_DEBUG_FW(xvt, "Alive VER4\n");
} else if (version == 5 || version == 6) {
/* v5 and v6 are compatible (only IMR addition) */
__le32 lmac2_err_ptr;
palive5 = (void *)pkt->data;
status = le16_to_cpu(palive5->status);
flags = le16_to_cpu(palive5->flags);
lmac1 = &palive5->lmac_data[0];
lmac2 = &palive5->lmac_data[1];
umac = &palive5->umac_data;
lmac2_err_ptr = lmac2->dbg_ptrs.error_event_table_ptr;
xvt->trans->dbg.lmac_error_event_table[1] =
le32_to_cpu(lmac2_err_ptr);
xvt->trans->sku_id[0] = le32_to_cpu(palive5->sku_id.data[0]);
xvt->trans->sku_id[1] = le32_to_cpu(palive5->sku_id.data[1]);
xvt->trans->sku_id[2] = le32_to_cpu(palive5->sku_id.data[2]);
IWL_DEBUG_FW(xvt,
"Alive VER%d - Got sku_id: 0x0%x 0x0%x 0x0%x\n",
version,
xvt->trans->sku_id[0],
xvt->trans->sku_id[1],
xvt->trans->sku_id[2]);
} else {
IWL_ERR(xvt, "unrecognized alive notificatio\n");
return false;
}
alive_data->valid = status == IWL_ALIVE_STATUS_OK;
lmac_error_event_table =
le32_to_cpu(lmac1->dbg_ptrs.error_event_table_ptr);
alive_data->scd_base_addr =
le32_to_cpu(lmac1->dbg_ptrs.scd_base_ptr);
iwl_tm_set_fw_ver(xvt->trans, le32_to_cpu(lmac1->ucode_major),
le32_to_cpu(lmac1->ucode_minor));
umac_error_event_table =
le32_to_cpu(umac->dbg_ptrs.error_info_addr);
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);
}
iwl_fw_lmac1_set_alive_err_table(xvt->trans, lmac_error_event_table);
if (umac_error_event_table)
iwl_fw_umac_set_alive_err_table(xvt->trans,
umac_error_event_table);
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 u16 alive_cmd[] = { UCODE_ALIVE_NTFY };
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;
if (xvt->sw_stack_cfg.fw_dbg_flags & ~IWL_XVT_DBG_FLAGS_NO_DEFAULT_TXQ)
return -EOPNOTSUPP;
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(xvt->trans, fw,
ucode_type == IWL_UCODE_INIT);
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_ERR(xvt, "XVT: ret:%d\n", 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;
ret = iwl_xvt_pnvm_load(xvt->trans, &xvt->notif_wait,
&xvt->fw->ucode_capa);
if (ret) {
IWL_ERR(xvt, "Timeout waiting for PNVM load!\n");
iwl_fw_set_current_image(&xvt->fwrt, old_type);
return ret;
}
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 &&
fw_has_capa(&xvt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_DQA_SUPPORT)) {
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) &&
ucode_type != IWL_UCODE_INIT) {
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;
#ifdef CPTCFG_IWLWIFI_DEBUGFS
iwl_fw_set_dbg_rec_on(&xvt->fwrt);
#endif
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);
}
static int iwl_xvt_config_ltr(struct iwl_xvt *xvt)
{
struct iwl_ltr_config_cmd cmd = {
.flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE),
};
if (!xvt->trans->ltr_enabled)
return 0;
return iwl_xvt_send_cmd_pdu(xvt, LTR_CONFIG, 0, sizeof(cmd), &cmd);
}
int iwl_xvt_run_fw(struct iwl_xvt *xvt, u32 ucode_type)
{
int ret;
if (ucode_type >= IWL_UCODE_TYPE_MAX)
return -EINVAL;
lockdep_assert_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_fw_dbg_stop_sync(&xvt->fwrt);
iwl_trans_stop_device(xvt->trans);
}
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);
iwl_dbg_tlv_time_point(&xvt->fwrt, IWL_FW_INI_TIME_POINT_EARLY, NULL);
/* 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_fw_dbg_stop_sync(&xvt->fwrt);
iwl_trans_stop_device(xvt->trans);
return ret;
}
iwl_dbg_tlv_time_point(&xvt->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE,
NULL);
iwl_fw_disable_dbg_asserts(&xvt->fwrt);
iwl_get_shared_mem_conf(&xvt->fwrt);
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_fw_dbg_stop_sync(&xvt->fwrt);
iwl_trans_stop_device(xvt->trans);
return ret;
}
}
iwl_dnt_start(xvt->trans);
if (xvt->fwrt.cur_fw_img == IWL_UCODE_REGULAR &&
(!fw_has_capa(&xvt->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_SET_LTR_GEN2)))
WARN_ON(iwl_xvt_config_ltr(xvt));
if (!iwl_trans_dbg_ini_valid(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;
}