Merge tag 'iwlwifi-next-for-kalle-2014-12-30' of https://git.kernel.org/pub/scm/linux...
[cascardo/linux.git] / drivers / net / wireless / iwlwifi / pcie / trans.c
index 5d79a1f..9ee4ca0 100644 (file)
@@ -443,10 +443,25 @@ static int iwl_pcie_apm_stop_master(struct iwl_trans *trans)
        return ret;
 }
 
-static void iwl_pcie_apm_stop(struct iwl_trans *trans)
+static void iwl_pcie_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_apm_init(trans);
+
+               /* inform ME that we are leaving */
+               if (trans->cfg->device_family == IWL_DEVICE_FAMILY_7000)
+                       iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG,
+                                         APMG_PCIDEV_STT_VAL_WAKE_ME);
+               else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+                       iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
+                                   CSR_HW_IF_CONFIG_REG_PREPARE |
+                                   CSR_HW_IF_CONFIG_REG_ENABLE_PME);
+               mdelay(5);
+       }
+
        clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
 
        /* Stop device's DMA activity */
@@ -614,7 +629,7 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num,
 {
        u8 *v_addr;
        dma_addr_t p_addr;
-       u32 offset, chunk_sz = section->len;
+       u32 offset, chunk_sz = min_t(u32, FH_MEM_TB_MAX_LENGTH, section->len);
        int ret = 0;
 
        IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n",
@@ -893,6 +908,9 @@ static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans,
        if (ret)
                return ret;
 
+       if (trans->dbg_dest_tlv)
+               iwl_pcie_apply_destination(trans);
+
        /* Notify FW loading is done */
        iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFFFFFF);
 
@@ -916,6 +934,7 @@ static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans,
 static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
                                   const struct fw_img *fw, bool run_in_rfkill)
 {
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int ret;
        bool hw_rfkill;
 
@@ -945,6 +964,9 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
                return ret;
        }
 
+       /* init ref_count to 1 (should be cleared when ucode is loaded) */
+       trans_pcie->ref_count = 1;
+
        /* make sure rfkill handshake bits are cleared */
        iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
        iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR,
@@ -1010,18 +1032,23 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
                      CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
 
        /* Stop the device, and put it in low power state */
-       iwl_pcie_apm_stop(trans);
+       iwl_pcie_apm_stop(trans, false);
 
-       /* Upon stop, the APM issues an interrupt if HW RF kill is set.
-        * Clean again the interrupt here
+       /* stop and reset the on-board processor */
+       iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+       udelay(20);
+
+       /*
+        * 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.
         */
        spin_lock(&trans_pcie->irq_lock);
        iwl_disable_interrupts(trans);
        spin_unlock(&trans_pcie->irq_lock);
 
-       /* stop and reset the on-board processor */
-       iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-       udelay(20);
 
        /* clear all status bits */
        clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
@@ -1187,7 +1214,7 @@ static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans)
        iwl_disable_interrupts(trans);
        spin_unlock(&trans_pcie->irq_lock);
 
-       iwl_pcie_apm_stop(trans);
+       iwl_pcie_apm_stop(trans, true);
 
        spin_lock(&trans_pcie->irq_lock);
        iwl_disable_interrupts(trans);
@@ -1535,6 +1562,38 @@ static void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg,
        spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
 }
 
+void iwl_trans_pcie_ref(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       unsigned long flags;
+
+       if (iwlwifi_mod_params.d0i3_disable)
+               return;
+
+       spin_lock_irqsave(&trans_pcie->ref_lock, flags);
+       IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count);
+       trans_pcie->ref_count++;
+       spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
+}
+
+void iwl_trans_pcie_unref(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       unsigned long flags;
+
+       if (iwlwifi_mod_params.d0i3_disable)
+               return;
+
+       spin_lock_irqsave(&trans_pcie->ref_lock, flags);
+       IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count);
+       if (WARN_ON_ONCE(trans_pcie->ref_count == 0)) {
+               spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
+               return;
+       }
+       trans_pcie->ref_count--;
+       spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
+}
+
 static const char *get_csr_string(int cmd)
 {
 #define IWL_CMD(x) case x: return #x
@@ -2259,6 +2318,9 @@ static const struct iwl_trans_ops trans_ops_pcie = {
        .release_nic_access = iwl_trans_pcie_release_nic_access,
        .set_bits_mask = iwl_trans_pcie_set_bits_mask,
 
+       .ref = iwl_trans_pcie_ref,
+       .unref = iwl_trans_pcie_unref,
+
        .dump_data = iwl_trans_pcie_dump_data,
 };
 
@@ -2399,6 +2461,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        }
 
        trans_pcie->inta_mask = CSR_INI_SET_MASK;
+       trans->d0i3_mode = IWL_D0I3_MODE_ON_SUSPEND;
 
        return trans;