162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017 Intel Deutschland GmbH 462306a36Sopenharmony_ci * Copyright (C) 2018-2023 Intel Corporation 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "iwl-trans.h" 762306a36Sopenharmony_ci#include "iwl-prph.h" 862306a36Sopenharmony_ci#include "iwl-context-info.h" 962306a36Sopenharmony_ci#include "iwl-context-info-gen3.h" 1062306a36Sopenharmony_ci#include "internal.h" 1162306a36Sopenharmony_ci#include "fw/dbg.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define FW_RESET_TIMEOUT (HZ / 5) 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* 1662306a36Sopenharmony_ci * Start up NIC's basic functionality after it has been reset 1762306a36Sopenharmony_ci * (e.g. after platform boot, or shutdown via iwl_pcie_apm_stop()) 1862306a36Sopenharmony_ci * NOTE: This does not load uCode nor start the embedded processor 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ciint iwl_pcie_gen2_apm_init(struct iwl_trans *trans) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci int ret = 0; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, "Init card's basic functions\n"); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* 2762306a36Sopenharmony_ci * Use "set_bit" below rather than "write", to preserve any hardware 2862306a36Sopenharmony_ci * bits already set by default after reset. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci /* 3262306a36Sopenharmony_ci * Disable L0s without affecting L1; 3362306a36Sopenharmony_ci * don't wait for ICH L0s (ICH bug W/A) 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, 3662306a36Sopenharmony_ci CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci /* Set FH wait threshold to maximum (HW error during stress W/A) */ 3962306a36Sopenharmony_ci iwl_set_bit(trans, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci /* 4262306a36Sopenharmony_ci * Enable HAP INTA (interrupt from management bus) to 4362306a36Sopenharmony_ci * wake device's PCI Express link L1a -> L0s 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, 4662306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci iwl_pcie_apm_config(trans); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci ret = iwl_finish_nic_init(trans); 5162306a36Sopenharmony_ci if (ret) 5262306a36Sopenharmony_ci return ret; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci set_bit(STATUS_DEVICE_ENABLED, &trans->status); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n"); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (op_mode_leave) { 6462306a36Sopenharmony_ci if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status)) 6562306a36Sopenharmony_ci iwl_pcie_gen2_apm_init(trans); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* inform ME that we are leaving */ 6862306a36Sopenharmony_ci iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, 6962306a36Sopenharmony_ci CSR_RESET_LINK_PWR_MGMT_DISABLED); 7062306a36Sopenharmony_ci iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, 7162306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_PREPARE | 7262306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_ENABLE_PME); 7362306a36Sopenharmony_ci mdelay(1); 7462306a36Sopenharmony_ci iwl_clear_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, 7562306a36Sopenharmony_ci CSR_RESET_LINK_PWR_MGMT_DISABLED); 7662306a36Sopenharmony_ci mdelay(5); 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci clear_bit(STATUS_DEVICE_ENABLED, &trans->status); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* Stop device's DMA activity */ 8262306a36Sopenharmony_ci iwl_pcie_apm_stop_master(trans); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci iwl_trans_sw_reset(trans, false); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * Clear "initialization complete" bit to move adapter from 8862306a36Sopenharmony_ci * D0A* (powered-up Active) --> D0U* (Uninitialized) state. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) 9162306a36Sopenharmony_ci iwl_clear_bit(trans, CSR_GP_CNTRL, 9262306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_INIT); 9362306a36Sopenharmony_ci else 9462306a36Sopenharmony_ci iwl_clear_bit(trans, CSR_GP_CNTRL, 9562306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_INIT_DONE); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 10162306a36Sopenharmony_ci int ret; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci trans_pcie->fw_reset_state = FW_RESET_REQUESTED; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) 10662306a36Sopenharmony_ci iwl_write_umac_prph(trans, UREG_NIC_SET_NMI_DRIVER, 10762306a36Sopenharmony_ci UREG_NIC_SET_NMI_DRIVER_RESET_HANDSHAKE); 10862306a36Sopenharmony_ci else if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) 10962306a36Sopenharmony_ci iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6, 11062306a36Sopenharmony_ci UREG_DOORBELL_TO_ISR6_RESET_HANDSHAKE); 11162306a36Sopenharmony_ci else 11262306a36Sopenharmony_ci iwl_write32(trans, CSR_DOORBELL_VECTOR, 11362306a36Sopenharmony_ci UREG_DOORBELL_TO_ISR6_RESET_HANDSHAKE); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* wait 200ms */ 11662306a36Sopenharmony_ci ret = wait_event_timeout(trans_pcie->fw_reset_waitq, 11762306a36Sopenharmony_ci trans_pcie->fw_reset_state != FW_RESET_REQUESTED, 11862306a36Sopenharmony_ci FW_RESET_TIMEOUT); 11962306a36Sopenharmony_ci if (!ret || trans_pcie->fw_reset_state == FW_RESET_ERROR) { 12062306a36Sopenharmony_ci u32 inta_hw = iwl_read32(trans, CSR_MSIX_HW_INT_CAUSES_AD); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci IWL_ERR(trans, 12362306a36Sopenharmony_ci "timeout waiting for FW reset ACK (inta_hw=0x%x)\n", 12462306a36Sopenharmony_ci inta_hw); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (!(inta_hw & MSIX_HW_INT_CAUSES_REG_RESET_DONE)) 12762306a36Sopenharmony_ci iwl_trans_fw_error(trans, true); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci trans_pcie->fw_reset_state = FW_RESET_IDLE; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_civoid _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci lockdep_assert_held(&trans_pcie->mutex); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (trans_pcie->is_down) 14062306a36Sopenharmony_ci return; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (trans->state >= IWL_TRANS_FW_STARTED) 14362306a36Sopenharmony_ci if (trans_pcie->fw_reset_handshake) 14462306a36Sopenharmony_ci iwl_trans_pcie_fw_reset_handshake(trans); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci trans_pcie->is_down = true; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* tell the device to stop sending interrupts */ 14962306a36Sopenharmony_ci iwl_disable_interrupts(trans); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* device going down, Stop using ICT table */ 15262306a36Sopenharmony_ci iwl_pcie_disable_ict(trans); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* 15562306a36Sopenharmony_ci * If a HW restart happens during firmware loading, 15662306a36Sopenharmony_ci * then the firmware loading might call this function 15762306a36Sopenharmony_ci * and later it might be called again due to the 15862306a36Sopenharmony_ci * restart. So don't process again if the device is 15962306a36Sopenharmony_ci * already dead. 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) { 16262306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, 16362306a36Sopenharmony_ci "DEVICE_ENABLED bit was set and is now cleared\n"); 16462306a36Sopenharmony_ci iwl_pcie_synchronize_irqs(trans); 16562306a36Sopenharmony_ci iwl_pcie_rx_napi_sync(trans); 16662306a36Sopenharmony_ci iwl_txq_gen2_tx_free(trans); 16762306a36Sopenharmony_ci iwl_pcie_rx_stop(trans); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci iwl_pcie_ctxt_info_free_paging(trans); 17162306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) 17262306a36Sopenharmony_ci iwl_pcie_ctxt_info_gen3_free(trans, false); 17362306a36Sopenharmony_ci else 17462306a36Sopenharmony_ci iwl_pcie_ctxt_info_free(trans); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* Stop the device, and put it in low power state */ 17762306a36Sopenharmony_ci iwl_pcie_gen2_apm_stop(trans, false); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* re-take ownership to prevent other users from stealing the device */ 18062306a36Sopenharmony_ci iwl_trans_sw_reset(trans, true); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* 18362306a36Sopenharmony_ci * Upon stop, the IVAR table gets erased, so msi-x won't 18462306a36Sopenharmony_ci * work. This causes a bug in RF-KILL flows, since the interrupt 18562306a36Sopenharmony_ci * that enables radio won't fire on the correct irq, and the 18662306a36Sopenharmony_ci * driver won't be able to handle the interrupt. 18762306a36Sopenharmony_ci * Configure the IVAR table again after reset. 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_ci iwl_pcie_conf_msix_hw(trans_pcie); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* 19262306a36Sopenharmony_ci * Upon stop, the APM issues an interrupt if HW RF kill is set. 19362306a36Sopenharmony_ci * This is a bug in certain verions of the hardware. 19462306a36Sopenharmony_ci * Certain devices also keep sending HW RF kill interrupt all 19562306a36Sopenharmony_ci * the time, unless the interrupt is ACKed even if the interrupt 19662306a36Sopenharmony_ci * should be masked. Re-ACK all the interrupts here. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci iwl_disable_interrupts(trans); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* clear all status bits */ 20162306a36Sopenharmony_ci clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); 20262306a36Sopenharmony_ci clear_bit(STATUS_INT_ENABLED, &trans->status); 20362306a36Sopenharmony_ci clear_bit(STATUS_TPOWER_PMI, &trans->status); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * Even if we stop the HW, we still want the RF kill 20762306a36Sopenharmony_ci * interrupt 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci iwl_enable_rfkill_int(trans); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_civoid iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 21562306a36Sopenharmony_ci bool was_in_rfkill; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci iwl_op_mode_time_point(trans->op_mode, 21862306a36Sopenharmony_ci IWL_FW_INI_TIME_POINT_HOST_DEVICE_DISABLE, 21962306a36Sopenharmony_ci NULL); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci mutex_lock(&trans_pcie->mutex); 22262306a36Sopenharmony_ci trans_pcie->opmode_down = true; 22362306a36Sopenharmony_ci was_in_rfkill = test_bit(STATUS_RFKILL_OPMODE, &trans->status); 22462306a36Sopenharmony_ci _iwl_trans_pcie_gen2_stop_device(trans); 22562306a36Sopenharmony_ci iwl_trans_pcie_handle_stop_rfkill(trans, was_in_rfkill); 22662306a36Sopenharmony_ci mutex_unlock(&trans_pcie->mutex); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int iwl_pcie_gen2_nic_init(struct iwl_trans *trans) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 23262306a36Sopenharmony_ci int queue_size = max_t(u32, IWL_CMD_QUEUE_SIZE, 23362306a36Sopenharmony_ci trans->cfg->min_txq_size); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* TODO: most of the logic can be removed in A0 - but not in Z0 */ 23662306a36Sopenharmony_ci spin_lock_bh(&trans_pcie->irq_lock); 23762306a36Sopenharmony_ci iwl_pcie_gen2_apm_init(trans); 23862306a36Sopenharmony_ci spin_unlock_bh(&trans_pcie->irq_lock); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci iwl_op_mode_nic_config(trans->op_mode); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* Allocate the RX queue, or reset if it is already allocated */ 24362306a36Sopenharmony_ci if (iwl_pcie_gen2_rx_init(trans)) 24462306a36Sopenharmony_ci return -ENOMEM; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Allocate or reset and init all Tx and Command queues */ 24762306a36Sopenharmony_ci if (iwl_txq_gen2_init(trans, trans->txqs.cmd.q_id, queue_size)) 24862306a36Sopenharmony_ci return -ENOMEM; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* enable shadow regs in HW */ 25162306a36Sopenharmony_ci iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, 0x800FFFFF); 25262306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, "Enabling shadow registers in device\n"); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return 0; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic void iwl_pcie_get_rf_name(struct iwl_trans *trans) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 26062306a36Sopenharmony_ci char *buf = trans_pcie->rf_name; 26162306a36Sopenharmony_ci size_t buflen = sizeof(trans_pcie->rf_name); 26262306a36Sopenharmony_ci size_t pos; 26362306a36Sopenharmony_ci u32 version; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (buf[0]) 26662306a36Sopenharmony_ci return; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) { 26962306a36Sopenharmony_ci case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF): 27062306a36Sopenharmony_ci pos = scnprintf(buf, buflen, "JF"); 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_GF): 27362306a36Sopenharmony_ci pos = scnprintf(buf, buflen, "GF"); 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_GF4): 27662306a36Sopenharmony_ci pos = scnprintf(buf, buflen, "GF4"); 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HR): 27962306a36Sopenharmony_ci pos = scnprintf(buf, buflen, "HR"); 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HR1): 28262306a36Sopenharmony_ci pos = scnprintf(buf, buflen, "HR1"); 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HRCDB): 28562306a36Sopenharmony_ci pos = scnprintf(buf, buflen, "HRCDB"); 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_MS): 28862306a36Sopenharmony_ci pos = scnprintf(buf, buflen, "MS"); 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci default: 29162306a36Sopenharmony_ci return; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) { 29562306a36Sopenharmony_ci case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HR): 29662306a36Sopenharmony_ci case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HR1): 29762306a36Sopenharmony_ci case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HRCDB): 29862306a36Sopenharmony_ci version = iwl_read_prph(trans, CNVI_MBOX_C); 29962306a36Sopenharmony_ci switch (version) { 30062306a36Sopenharmony_ci case 0x20000: 30162306a36Sopenharmony_ci pos += scnprintf(buf + pos, buflen - pos, " B3"); 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci case 0x120000: 30462306a36Sopenharmony_ci pos += scnprintf(buf + pos, buflen - pos, " B5"); 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci default: 30762306a36Sopenharmony_ci pos += scnprintf(buf + pos, buflen - pos, 30862306a36Sopenharmony_ci " (0x%x)", version); 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci break; 31262306a36Sopenharmony_ci default: 31362306a36Sopenharmony_ci break; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci pos += scnprintf(buf + pos, buflen - pos, ", rfid=0x%x", 31762306a36Sopenharmony_ci trans->hw_rf_id); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci IWL_INFO(trans, "Detected RF %s\n", buf); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* 32262306a36Sopenharmony_ci * also add a \n for debugfs - need to do it after printing 32362306a36Sopenharmony_ci * since our IWL_INFO machinery wants to see a static \n at 32462306a36Sopenharmony_ci * the end of the string 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_ci pos += scnprintf(buf + pos, buflen - pos, "\n"); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_civoid iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans, u32 scd_addr) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci iwl_pcie_reset_ict(trans); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* make sure all queue are not stopped/used */ 33662306a36Sopenharmony_ci memset(trans->txqs.queue_stopped, 0, 33762306a36Sopenharmony_ci sizeof(trans->txqs.queue_stopped)); 33862306a36Sopenharmony_ci memset(trans->txqs.queue_used, 0, sizeof(trans->txqs.queue_used)); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* now that we got alive we can free the fw image & the context info. 34162306a36Sopenharmony_ci * paging memory cannot be freed included since FW will still use it 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) 34462306a36Sopenharmony_ci iwl_pcie_ctxt_info_gen3_free(trans, true); 34562306a36Sopenharmony_ci else 34662306a36Sopenharmony_ci iwl_pcie_ctxt_info_free(trans); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* 34962306a36Sopenharmony_ci * Re-enable all the interrupts, including the RF-Kill one, now that 35062306a36Sopenharmony_ci * the firmware is alive. 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_ci iwl_enable_interrupts(trans); 35362306a36Sopenharmony_ci mutex_lock(&trans_pcie->mutex); 35462306a36Sopenharmony_ci iwl_pcie_check_hw_rf_kill(trans); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci iwl_pcie_get_rf_name(trans); 35762306a36Sopenharmony_ci mutex_unlock(&trans_pcie->mutex); 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic bool iwl_pcie_set_ltr(struct iwl_trans *trans) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci u32 ltr_val = CSR_LTR_LONG_VAL_AD_NO_SNOOP_REQ | 36362306a36Sopenharmony_ci u32_encode_bits(CSR_LTR_LONG_VAL_AD_SCALE_USEC, 36462306a36Sopenharmony_ci CSR_LTR_LONG_VAL_AD_NO_SNOOP_SCALE) | 36562306a36Sopenharmony_ci u32_encode_bits(250, 36662306a36Sopenharmony_ci CSR_LTR_LONG_VAL_AD_NO_SNOOP_VAL) | 36762306a36Sopenharmony_ci CSR_LTR_LONG_VAL_AD_SNOOP_REQ | 36862306a36Sopenharmony_ci u32_encode_bits(CSR_LTR_LONG_VAL_AD_SCALE_USEC, 36962306a36Sopenharmony_ci CSR_LTR_LONG_VAL_AD_SNOOP_SCALE) | 37062306a36Sopenharmony_ci u32_encode_bits(250, CSR_LTR_LONG_VAL_AD_SNOOP_VAL); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* 37362306a36Sopenharmony_ci * To workaround hardware latency issues during the boot process, 37462306a36Sopenharmony_ci * initialize the LTR to ~250 usec (see ltr_val above). 37562306a36Sopenharmony_ci * The firmware initializes this again later (to a smaller value). 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_ci if ((trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210 || 37862306a36Sopenharmony_ci trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000) && 37962306a36Sopenharmony_ci !trans->trans_cfg->integrated) { 38062306a36Sopenharmony_ci iwl_write32(trans, CSR_LTR_LONG_VAL_AD, ltr_val); 38162306a36Sopenharmony_ci return true; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (trans->trans_cfg->integrated && 38562306a36Sopenharmony_ci trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000) { 38662306a36Sopenharmony_ci iwl_write_prph(trans, HPM_MAC_LTR_CSR, HPM_MAC_LRT_ENABLE_ALL); 38762306a36Sopenharmony_ci iwl_write_prph(trans, HPM_UMAC_LTR, ltr_val); 38862306a36Sopenharmony_ci return true; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) { 39262306a36Sopenharmony_ci /* First clear the interrupt, just in case */ 39362306a36Sopenharmony_ci iwl_write32(trans, CSR_MSIX_HW_INT_CAUSES_AD, 39462306a36Sopenharmony_ci MSIX_HW_INT_CAUSES_REG_IML); 39562306a36Sopenharmony_ci /* In this case, unfortunately the same ROM bug exists in the 39662306a36Sopenharmony_ci * device (not setting LTR correctly), but we don't have control 39762306a36Sopenharmony_ci * over the settings from the host due to some hardware security 39862306a36Sopenharmony_ci * features. The only workaround we've been able to come up with 39962306a36Sopenharmony_ci * so far is to try to keep the CPU and device busy by polling 40062306a36Sopenharmony_ci * it and the IML (image loader) completed interrupt. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci return false; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* nothing needs to be done on other devices */ 40662306a36Sopenharmony_ci return true; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic void iwl_pcie_spin_for_iml(struct iwl_trans *trans) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci/* in practice, this seems to complete in around 20-30ms at most, wait 100 */ 41262306a36Sopenharmony_ci#define IML_WAIT_TIMEOUT (HZ / 10) 41362306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 41462306a36Sopenharmony_ci unsigned long end_time = jiffies + IML_WAIT_TIMEOUT; 41562306a36Sopenharmony_ci u32 value, loops = 0; 41662306a36Sopenharmony_ci bool irq = false; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (WARN_ON(!trans_pcie->iml)) 41962306a36Sopenharmony_ci return; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci value = iwl_read32(trans, CSR_LTR_LAST_MSG); 42262306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, "Polling for IML load - CSR_LTR_LAST_MSG=0x%x\n", 42362306a36Sopenharmony_ci value); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci while (time_before(jiffies, end_time)) { 42662306a36Sopenharmony_ci if (iwl_read32(trans, CSR_MSIX_HW_INT_CAUSES_AD) & 42762306a36Sopenharmony_ci MSIX_HW_INT_CAUSES_REG_IML) { 42862306a36Sopenharmony_ci irq = true; 42962306a36Sopenharmony_ci break; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci /* Keep the CPU and device busy. */ 43262306a36Sopenharmony_ci value = iwl_read32(trans, CSR_LTR_LAST_MSG); 43362306a36Sopenharmony_ci loops++; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, 43762306a36Sopenharmony_ci "Polled for IML load: irq=%d, loops=%d, CSR_LTR_LAST_MSG=0x%x\n", 43862306a36Sopenharmony_ci irq, loops, value); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* We don't fail here even if we timed out - maybe we get lucky and the 44162306a36Sopenharmony_ci * interrupt comes in later (and we get alive from firmware) and then 44262306a36Sopenharmony_ci * we're all happy - but if not we'll fail on alive timeout or get some 44362306a36Sopenharmony_ci * other error out. 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ciint iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, 44862306a36Sopenharmony_ci const struct fw_img *fw, bool run_in_rfkill) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 45162306a36Sopenharmony_ci bool hw_rfkill, keep_ram_busy; 45262306a36Sopenharmony_ci int ret; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* This may fail if AMT took ownership of the device */ 45562306a36Sopenharmony_ci if (iwl_pcie_prepare_card_hw(trans)) { 45662306a36Sopenharmony_ci IWL_WARN(trans, "Exit HW not ready\n"); 45762306a36Sopenharmony_ci return -EIO; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci iwl_enable_rfkill_int(trans); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci iwl_write32(trans, CSR_INT, 0xFFFFFFFF); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* 46562306a36Sopenharmony_ci * We enabled the RF-Kill interrupt and the handler may very 46662306a36Sopenharmony_ci * well be running. Disable the interrupts to make sure no other 46762306a36Sopenharmony_ci * interrupt can be fired. 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ci iwl_disable_interrupts(trans); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* Make sure it finished running */ 47262306a36Sopenharmony_ci iwl_pcie_synchronize_irqs(trans); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci mutex_lock(&trans_pcie->mutex); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* If platform's RF_KILL switch is NOT set to KILL */ 47762306a36Sopenharmony_ci hw_rfkill = iwl_pcie_check_hw_rf_kill(trans); 47862306a36Sopenharmony_ci if (hw_rfkill && !run_in_rfkill) { 47962306a36Sopenharmony_ci ret = -ERFKILL; 48062306a36Sopenharmony_ci goto out; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* Someone called stop_device, don't try to start_fw */ 48462306a36Sopenharmony_ci if (trans_pcie->is_down) { 48562306a36Sopenharmony_ci IWL_WARN(trans, 48662306a36Sopenharmony_ci "Can't start_fw since the HW hasn't been started\n"); 48762306a36Sopenharmony_ci ret = -EIO; 48862306a36Sopenharmony_ci goto out; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* make sure rfkill handshake bits are cleared */ 49262306a36Sopenharmony_ci iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); 49362306a36Sopenharmony_ci iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, 49462306a36Sopenharmony_ci CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* clear (again), then enable host interrupts */ 49762306a36Sopenharmony_ci iwl_write32(trans, CSR_INT, 0xFFFFFFFF); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci ret = iwl_pcie_gen2_nic_init(trans); 50062306a36Sopenharmony_ci if (ret) { 50162306a36Sopenharmony_ci IWL_ERR(trans, "Unable to init nic\n"); 50262306a36Sopenharmony_ci goto out; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) 50662306a36Sopenharmony_ci ret = iwl_pcie_ctxt_info_gen3_init(trans, fw); 50762306a36Sopenharmony_ci else 50862306a36Sopenharmony_ci ret = iwl_pcie_ctxt_info_init(trans, fw); 50962306a36Sopenharmony_ci if (ret) 51062306a36Sopenharmony_ci goto out; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci keep_ram_busy = !iwl_pcie_set_ltr(trans); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { 51562306a36Sopenharmony_ci iwl_write32(trans, CSR_FUNC_SCRATCH, CSR_FUNC_SCRATCH_INIT_VALUE); 51662306a36Sopenharmony_ci iwl_set_bit(trans, CSR_GP_CNTRL, 51762306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_ROM_START); 51862306a36Sopenharmony_ci } else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { 51962306a36Sopenharmony_ci iwl_write_umac_prph(trans, UREG_CPU_INIT_RUN, 1); 52062306a36Sopenharmony_ci } else { 52162306a36Sopenharmony_ci iwl_write_prph(trans, UREG_CPU_INIT_RUN, 1); 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (keep_ram_busy) 52562306a36Sopenharmony_ci iwl_pcie_spin_for_iml(trans); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* re-check RF-Kill state since we may have missed the interrupt */ 52862306a36Sopenharmony_ci hw_rfkill = iwl_pcie_check_hw_rf_kill(trans); 52962306a36Sopenharmony_ci if (hw_rfkill && !run_in_rfkill) 53062306a36Sopenharmony_ci ret = -ERFKILL; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ciout: 53362306a36Sopenharmony_ci mutex_unlock(&trans_pcie->mutex); 53462306a36Sopenharmony_ci return ret; 53562306a36Sopenharmony_ci} 536