|
@@ -111,6 +111,153 @@ static int iwl_pcie_gen2_apm_init(struct iwl_trans *trans)
|
|
return 0;
|
|
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);
|
|
|
|
+
|
|
|
|
+ /* inform ME that we are leaving */
|
|
|
|
+ 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_PREPARE |
|
|
|
|
+ CSR_HW_IF_CONFIG_REG_ENABLE_PME);
|
|
|
|
+ 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);
|
|
|
|
+
|
|
|
|
+ /* Stop device's DMA activity */
|
|
|
|
+ iwl_pcie_apm_stop_master(trans);
|
|
|
|
+
|
|
|
|
+ /* Reset the entire device */
|
|
|
|
+ iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
|
|
|
|
+ usleep_range(1000, 2000);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Clear "initialization complete" bit to move adapter from
|
|
|
|
+ * D0A* (powered-up Active) --> D0U* (Uninitialized) state.
|
|
|
|
+ */
|
|
|
|
+ iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans, bool low_power)
|
|
|
|
+{
|
|
|
|
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
|
|
|
+ bool hw_rfkill, was_hw_rfkill;
|
|
|
|
+
|
|
|
|
+ lockdep_assert_held(&trans_pcie->mutex);
|
|
|
|
+
|
|
|
|
+ if (trans_pcie->is_down)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ trans_pcie->is_down = true;
|
|
|
|
+
|
|
|
|
+ was_hw_rfkill = iwl_is_rfkill_set(trans);
|
|
|
|
+
|
|
|
|
+ /* tell the device to stop sending interrupts */
|
|
|
|
+ iwl_disable_interrupts(trans);
|
|
|
|
+
|
|
|
|
+ /* device going down, Stop using ICT table */
|
|
|
|
+ iwl_pcie_disable_ict(trans);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * If a HW restart happens during firmware loading,
|
|
|
|
+ * then the firmware loading might call this function
|
|
|
|
+ * and later it might be called again due to the
|
|
|
|
+ * restart. So don't process again if the device is
|
|
|
|
+ * already dead.
|
|
|
|
+ */
|
|
|
|
+ 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_tx_stop(trans);
|
|
|
|
+ iwl_pcie_rx_stop(trans);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ iwl_pcie_ctxt_info_free_paging(trans);
|
|
|
|
+ iwl_pcie_ctxt_info_free(trans);
|
|
|
|
+
|
|
|
|
+ /* Make sure (redundant) we've released our request to stay awake */
|
|
|
|
+ iwl_clear_bit(trans, CSR_GP_CNTRL,
|
|
|
|
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
|
|
|
|
+
|
|
|
|
+ /* Stop the device, and put it in low power state */
|
|
|
|
+ iwl_pcie_gen2_apm_stop(trans, false);
|
|
|
|
+
|
|
|
|
+ /* stop and reset the on-board processor */
|
|
|
|
+ iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
|
|
|
|
+ usleep_range(1000, 2000);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Upon stop, the IVAR table gets erased, so msi-x won't
|
|
|
|
+ * work. This causes a bug in RF-KILL flows, since the interrupt
|
|
|
|
+ * that enables radio won't fire on the correct irq, and the
|
|
|
|
+ * driver won't be able to handle the interrupt.
|
|
|
|
+ * Configure the IVAR table again after reset.
|
|
|
|
+ */
|
|
|
|
+ iwl_pcie_conf_msix_hw(trans_pcie);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Upon stop, the APM issues an interrupt if HW RF kill is set.
|
|
|
|
+ * This is a bug in certain verions of the hardware.
|
|
|
|
+ * Certain devices also keep sending HW RF kill interrupt all
|
|
|
|
+ * the time, unless the interrupt is ACKed even if the interrupt
|
|
|
|
+ * should be masked. Re-ACK all the interrupts here.
|
|
|
|
+ */
|
|
|
|
+ iwl_disable_interrupts(trans);
|
|
|
|
+
|
|
|
|
+ /* clear all status bits */
|
|
|
|
+ clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
|
|
|
|
+ clear_bit(STATUS_INT_ENABLED, &trans->status);
|
|
|
|
+ clear_bit(STATUS_TPOWER_PMI, &trans->status);
|
|
|
|
+ clear_bit(STATUS_RFKILL, &trans->status);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Even if we stop the HW, we still want the RF kill
|
|
|
|
+ * interrupt
|
|
|
|
+ */
|
|
|
|
+ iwl_enable_rfkill_int(trans);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Check again since the RF kill state may have changed while
|
|
|
|
+ * all the interrupts were disabled, in this case we couldn't
|
|
|
|
+ * receive the RF kill interrupt and update the state in the
|
|
|
|
+ * op_mode.
|
|
|
|
+ * Don't call the op_mode if the rkfill state hasn't changed.
|
|
|
|
+ * This allows the op_mode to call stop_device from the rfkill
|
|
|
|
+ * notification without endless recursion. Under very rare
|
|
|
|
+ * circumstances, we might have a small recursion if the rfkill
|
|
|
|
+ * state changed exactly now while we were called from stop_device.
|
|
|
|
+ * This is very unlikely but can happen and is supported.
|
|
|
|
+ */
|
|
|
|
+ hw_rfkill = iwl_is_rfkill_set(trans);
|
|
|
|
+ if (hw_rfkill)
|
|
|
|
+ set_bit(STATUS_RFKILL, &trans->status);
|
|
|
|
+ else
|
|
|
|
+ clear_bit(STATUS_RFKILL, &trans->status);
|
|
|
|
+ if (hw_rfkill != was_hw_rfkill)
|
|
|
|
+ iwl_trans_pcie_rf_kill(trans, hw_rfkill);
|
|
|
|
+
|
|
|
|
+ /* re-take ownership to prevent other users from stealing the device */
|
|
|
|
+ iwl_pcie_prepare_card_hw(trans);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans, bool low_power)
|
|
|
|
+{
|
|
|
|
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
|
|
|
+
|
|
|
|
+ mutex_lock(&trans_pcie->mutex);
|
|
|
|
+ _iwl_trans_pcie_gen2_stop_device(trans, low_power);
|
|
|
|
+ mutex_unlock(&trans_pcie->mutex);
|
|
|
|
+}
|
|
|
|
+
|
|
static int iwl_pcie_gen2_nic_init(struct iwl_trans *trans)
|
|
static int iwl_pcie_gen2_nic_init(struct iwl_trans *trans)
|
|
{
|
|
{
|
|
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
|
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|