#include "iwl-trans.h"
#include "iwl-prph.h"
#include "pcie/iwl-context-info.h"
#include "pcie/iwl-context-info-v2.h"
#include "internal.h"
#include "fw/dbg.h"
#define FW_RESET_TIMEOUT (HZ / 5)
int iwl_pcie_gen2_apm_init(struct iwl_trans *trans)
{
int ret = 0;
IWL_DEBUG_INFO(trans, "Init card's basic functions\n");
iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS,
CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
iwl_set_bit(trans, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL);
iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_HAP_WAKE);
iwl_pcie_apm_config(trans);
ret = iwl_trans_activate_nic(trans);
if (ret)
return ret;
set_bit(STATUS_DEVICE_ENABLED, &trans->status);
return 0;
}
static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
{
IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n");
if (op_mode_leave) {
if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status))
iwl_pcie_gen2_apm_init(trans);
iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG,
CSR_RESET_LINK_PWR_MGMT_DISABLED);
iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_WAKE_ME |
CSR_HW_IF_CONFIG_REG_WAKE_ME_PCIE_OWNER_EN);
mdelay(1);
iwl_clear_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG,
CSR_RESET_LINK_PWR_MGMT_DISABLED);
mdelay(5);
}
clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
iwl_pcie_apm_stop_master(trans);
iwl_trans_pcie_sw_reset(trans, false);
if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
iwl_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_INIT);
else
iwl_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
}
void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int ret;
trans_pcie->fw_reset_state = FW_RESET_REQUESTED;
if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
iwl_write_umac_prph(trans, UREG_NIC_SET_NMI_DRIVER,
UREG_NIC_SET_NMI_DRIVER_RESET_HANDSHAKE);
else if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_AX210)
iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
UREG_DOORBELL_TO_ISR6_RESET_HANDSHAKE);
else
iwl_write32(trans, CSR_DOORBELL_VECTOR,
UREG_DOORBELL_TO_ISR6_RESET_HANDSHAKE);
ret = wait_event_timeout(trans_pcie->fw_reset_waitq,
trans_pcie->fw_reset_state != FW_RESET_REQUESTED,
FW_RESET_TIMEOUT);
if (!ret || trans_pcie->fw_reset_state == FW_RESET_ERROR) {
bool reset_done;
u32 inta_hw;
if (trans_pcie->msix_enabled) {
inta_hw = iwl_read32(trans, CSR_MSIX_HW_INT_CAUSES_AD);
reset_done =
inta_hw & MSIX_HW_INT_CAUSES_REG_RESET_DONE;
} else {
inta_hw = iwl_read32(trans, CSR_INT);
reset_done = inta_hw & CSR_INT_BIT_RESET_DONE;
}
IWL_ERR(trans,
"timeout waiting for FW reset ACK (inta_hw=0x%x, reset_done %d)\n",
inta_hw, reset_done);
if (!reset_done) {
struct iwl_fw_error_dump_mode mode = {
.type = IWL_ERR_TYPE_RESET_HS_TIMEOUT,
.context = IWL_ERR_CONTEXT_FROM_OPMODE,
};
iwl_op_mode_nic_error(trans->op_mode,
IWL_ERR_TYPE_RESET_HS_TIMEOUT);
iwl_op_mode_dump_error(trans->op_mode, &mode);
}
}
trans_pcie->fw_reset_state = FW_RESET_IDLE;
}
static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
lockdep_assert_held(&trans_pcie->mutex);
if (trans_pcie->is_down)
return;
if (trans->state >= IWL_TRANS_FW_STARTED &&
trans->conf.fw_reset_handshake) {
trans->state = IWL_TRANS_NO_FW;
iwl_trans_pcie_fw_reset_handshake(trans);
}
trans_pcie->is_down = true;
iwl_disable_interrupts(trans);
iwl_pcie_disable_ict(trans);
if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
IWL_DEBUG_INFO(trans,
"DEVICE_ENABLED bit was set and is now cleared\n");
iwl_pcie_synchronize_irqs(trans);
iwl_pcie_rx_napi_sync(trans);
iwl_txq_gen2_tx_free(trans);
iwl_pcie_rx_stop(trans);
}
iwl_pcie_ctxt_info_free_paging(trans);
if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
iwl_pcie_ctxt_info_v2_free(trans, false);
else
iwl_pcie_ctxt_info_free(trans);
iwl_pcie_gen2_apm_stop(trans, false);
iwl_trans_pcie_sw_reset(trans, true);
iwl_pcie_conf_msix_hw(trans_pcie);
iwl_disable_interrupts(trans);
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
clear_bit(STATUS_INT_ENABLED, &trans->status);
clear_bit(STATUS_TPOWER_PMI, &trans->status);
iwl_enable_rfkill_int(trans);
}
void iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
bool was_in_rfkill;
iwl_op_mode_time_point(trans->op_mode,
IWL_FW_INI_TIME_POINT_HOST_DEVICE_DISABLE,
NULL);
mutex_lock(&trans_pcie->mutex);
trans_pcie->opmode_down = true;
was_in_rfkill = test_bit(STATUS_RFKILL_OPMODE, &trans->status);
_iwl_trans_pcie_gen2_stop_device(trans);
iwl_trans_pcie_handle_stop_rfkill(trans, was_in_rfkill);
mutex_unlock(&trans_pcie->mutex);
}
static int iwl_pcie_gen2_nic_init(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int queue_size = max_t(u32, IWL_CMD_QUEUE_SIZE,
trans->mac_cfg->base->min_txq_size);
int ret;
spin_lock_bh(&trans_pcie->irq_lock);
ret = iwl_pcie_gen2_apm_init(trans);
spin_unlock_bh(&trans_pcie->irq_lock);
if (ret)
return ret;
iwl_op_mode_nic_config(trans->op_mode);
if (iwl_pcie_gen2_rx_init(trans))
return -ENOMEM;
if (iwl_txq_gen2_init(trans, trans->conf.cmd_queue, queue_size))
return -ENOMEM;
iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, 0x800FFFFF);
IWL_DEBUG_INFO(trans, "Enabling shadow registers in device\n");
return 0;
}
static void iwl_pcie_get_rf_name(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
char *buf = trans_pcie->rf_name;
size_t buflen = sizeof(trans_pcie->rf_name);
size_t pos;
u32 version;
if (buf[0])
return;
switch (CSR_HW_RFID_TYPE(trans->info.hw_rf_id)) {
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF):
pos = scnprintf(buf, buflen, "JF");
break;
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_GF):
pos = scnprintf(buf, buflen, "GF");
break;
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_GF4):
pos = scnprintf(buf, buflen, "GF4");
break;
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HR):
pos = scnprintf(buf, buflen, "HR");
break;
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HR1):
pos = scnprintf(buf, buflen, "HR1");
break;
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HRCDB):
pos = scnprintf(buf, buflen, "HRCDB");
break;
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_FM):
pos = scnprintf(buf, buflen, "FM");
break;
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_WP):
if (SILICON_Z_STEP ==
CSR_HW_RFID_STEP(trans->info.hw_rf_id))
pos = scnprintf(buf, buflen, "WHTC");
else
pos = scnprintf(buf, buflen, "WH");
break;
default:
return;
}
switch (CSR_HW_RFID_TYPE(trans->info.hw_rf_id)) {
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HR):
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HR1):
case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HRCDB):
version = iwl_read_prph(trans, CNVI_MBOX_C);
switch (version) {
case 0x20000:
pos += scnprintf(buf + pos, buflen - pos, " B3");
break;
case 0x120000:
pos += scnprintf(buf + pos, buflen - pos, " B5");
break;
default:
pos += scnprintf(buf + pos, buflen - pos,
" (0x%x)", version);
break;
}
break;
default:
break;
}
pos += scnprintf(buf + pos, buflen - pos, ", rfid=0x%x",
trans->info.hw_rf_id);
IWL_INFO(trans, "Detected RF %s\n", buf);
pos += scnprintf(buf + pos, buflen - pos, "\n");
}
void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
iwl_pcie_reset_ict(trans);
memset(trans_pcie->txqs.queue_stopped, 0,
sizeof(trans_pcie->txqs.queue_stopped));
memset(trans_pcie->txqs.queue_used, 0,
sizeof(trans_pcie->txqs.queue_used));
if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
iwl_pcie_ctxt_info_v2_free(trans, true);
else
iwl_pcie_ctxt_info_free(trans);
iwl_enable_interrupts(trans);
mutex_lock(&trans_pcie->mutex);
iwl_pcie_check_hw_rf_kill(trans);
iwl_pcie_get_rf_name(trans);
mutex_unlock(&trans_pcie->mutex);
if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
trans->step_urm = !!(iwl_read_umac_prph(trans,
CNVI_PMU_STEP_FLOW) &
CNVI_PMU_STEP_FLOW_FORCE_URM);
}
static bool iwl_pcie_set_ltr(struct iwl_trans *trans)
{
u32 ltr_val = CSR_LTR_LONG_VAL_AD_NO_SNOOP_REQ |
u32_encode_bits(CSR_LTR_LONG_VAL_AD_SCALE_USEC,
CSR_LTR_LONG_VAL_AD_NO_SNOOP_SCALE) |
u32_encode_bits(250,
CSR_LTR_LONG_VAL_AD_NO_SNOOP_VAL) |
CSR_LTR_LONG_VAL_AD_SNOOP_REQ |
u32_encode_bits(CSR_LTR_LONG_VAL_AD_SCALE_USEC,
CSR_LTR_LONG_VAL_AD_SNOOP_SCALE) |
u32_encode_bits(250, CSR_LTR_LONG_VAL_AD_SNOOP_VAL);
if ((trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_AX210 ||
trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_22000) &&
!trans->mac_cfg->integrated) {
iwl_write32(trans, CSR_LTR_LONG_VAL_AD, ltr_val);
return true;
}
if (trans->mac_cfg->integrated &&
trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_22000) {
iwl_write_prph(trans, HPM_MAC_LTR_CSR, HPM_MAC_LRT_ENABLE_ALL);
iwl_write_prph(trans, HPM_UMAC_LTR, ltr_val);
return true;
}
if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_AX210) {
iwl_write32(trans, CSR_MSIX_HW_INT_CAUSES_AD,
MSIX_HW_INT_CAUSES_REG_IML);
return false;
}
return true;
}
static void iwl_pcie_spin_for_iml(struct iwl_trans *trans)
{
#define IML_WAIT_TIMEOUT (HZ / 10)
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
unsigned long end_time = jiffies + IML_WAIT_TIMEOUT;
u32 value, loops = 0;
bool irq = false;
if (WARN_ON(!trans_pcie->iml))
return;
value = iwl_read32(trans, CSR_LTR_LAST_MSG);
IWL_DEBUG_INFO(trans, "Polling for IML load - CSR_LTR_LAST_MSG=0x%x\n",
value);
while (time_before(jiffies, end_time)) {
if (iwl_read32(trans, CSR_MSIX_HW_INT_CAUSES_AD) &
MSIX_HW_INT_CAUSES_REG_IML) {
irq = true;
break;
}
value = iwl_read32(trans, CSR_LTR_LAST_MSG);
loops++;
}
IWL_DEBUG_INFO(trans,
"Polled for IML load: irq=%d, loops=%d, CSR_LTR_LAST_MSG=0x%x\n",
irq, loops, value);
}
int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans,
const struct iwl_fw *fw,
const struct fw_img *img,
bool run_in_rfkill)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
bool hw_rfkill, keep_ram_busy;
bool top_reset_done = false;
int ret;
mutex_lock(&trans_pcie->mutex);
again:
if (iwl_pcie_prepare_card_hw(trans)) {
IWL_WARN(trans, "Exit HW not ready\n");
ret = -EIO;
goto out;
}
iwl_enable_rfkill_int(trans);
iwl_write32(trans, CSR_INT, 0xFFFFFFFF);
iwl_disable_interrupts(trans);
iwl_pcie_synchronize_irqs(trans);
hw_rfkill = iwl_pcie_check_hw_rf_kill(trans);
if (hw_rfkill && !run_in_rfkill) {
ret = -ERFKILL;
goto out;
}
if (trans_pcie->is_down) {
IWL_WARN(trans,
"Can't start_fw since the HW hasn't been started\n");
ret = -EIO;
goto out;
}
iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR,
CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
iwl_write32(trans, CSR_INT, 0xFFFFFFFF);
ret = iwl_pcie_gen2_nic_init(trans);
if (ret) {
IWL_ERR(trans, "Unable to init nic\n");
goto out;
}
if (WARN_ON(trans->do_top_reset &&
trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC)) {
ret = -EINVAL;
goto out;
}
if (trans->do_top_reset)
trans_pcie->fw_reset_state = FW_RESET_TOP_REQUESTED;
if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
if (!top_reset_done) {
ret = iwl_pcie_ctxt_info_v2_alloc(trans, fw, img);
if (ret)
goto out;
}
iwl_pcie_ctxt_info_v2_kick(trans);
} else {
ret = iwl_pcie_ctxt_info_init(trans, img);
if (ret)
goto out;
}
keep_ram_busy = !iwl_pcie_set_ltr(trans);
if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
IWL_DEBUG_POWER(trans, "function scratch register value is 0x%08x\n",
iwl_read32(trans, CSR_FUNC_SCRATCH));
iwl_write32(trans, CSR_FUNC_SCRATCH, CSR_FUNC_SCRATCH_INIT_VALUE);
iwl_set_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_ROM_START);
} else if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
iwl_write_umac_prph(trans, UREG_CPU_INIT_RUN, 1);
} else {
iwl_write_prph(trans, UREG_CPU_INIT_RUN, 1);
}
if (keep_ram_busy)
iwl_pcie_spin_for_iml(trans);
if (trans->do_top_reset) {
trans->do_top_reset = 0;
#define FW_TOP_RESET_TIMEOUT (HZ / 4)
ret = wait_event_timeout(trans_pcie->fw_reset_waitq,
trans_pcie->fw_reset_state != FW_RESET_TOP_REQUESTED,
FW_TOP_RESET_TIMEOUT);
if (trans_pcie->fw_reset_state != FW_RESET_OK) {
if (trans_pcie->fw_reset_state != FW_RESET_TOP_REQUESTED)
IWL_ERR(trans,
"TOP reset interrupted by error (state %d)!\n",
trans_pcie->fw_reset_state);
else
IWL_ERR(trans, "TOP reset timed out!\n");
iwl_op_mode_nic_error(trans->op_mode,
IWL_ERR_TYPE_TOP_RESET_FAILED);
iwl_trans_schedule_reset(trans,
IWL_ERR_TYPE_TOP_RESET_FAILED);
ret = -EIO;
goto out;
}
msleep(10);
IWL_INFO(trans, "TOP reset successful, reinit now\n");
ret = _iwl_trans_pcie_start_hw(trans);
if (ret) {
IWL_ERR(trans, "failed to start HW after TOP reset\n");
goto out;
}
trans_pcie->prph_scratch->ctrl_cfg.control.control_flags &=
~cpu_to_le32(IWL_PRPH_SCRATCH_TOP_RESET);
top_reset_done = true;
goto again;
}
hw_rfkill = iwl_pcie_check_hw_rf_kill(trans);
if (hw_rfkill && !run_in_rfkill)
ret = -ERFKILL;
out:
mutex_unlock(&trans_pcie->mutex);
return ret;
}
void iwl_trans_pcie_gen2_op_mode_leave(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
mutex_lock(&trans_pcie->mutex);
iwl_disable_interrupts(trans);
iwl_pcie_gen2_apm_stop(trans, true);
iwl_disable_interrupts(trans);
iwl_pcie_disable_ict(trans);
mutex_unlock(&trans_pcie->mutex);
iwl_pcie_synchronize_irqs(trans);
}