iwlwifi: decouple PCIe transport from mac80211
[cascardo/linux.git] / drivers / net / wireless / intel / iwlwifi / pcie / tx.c
index 16ad820..2b68826 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/ieee80211.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
+#include <linux/pm_runtime.h>
 #include <net/ip6_checksum.h>
 #include <net/tso.h>
 
@@ -583,19 +584,41 @@ static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq,
        return 0;
 }
 
-static void iwl_pcie_free_tso_page(struct sk_buff *skb)
+static void iwl_pcie_free_tso_page(struct iwl_trans_pcie *trans_pcie,
+                                  struct sk_buff *skb)
 {
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct page **page_ptr;
 
-       if (info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA]) {
-               struct page *page =
-                       info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA];
+       page_ptr = (void *)((u8 *)skb->cb + trans_pcie->page_offs);
 
-               __free_page(page);
-               info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA] = NULL;
+       if (*page_ptr) {
+               __free_page(*page_ptr);
+               *page_ptr = NULL;
        }
 }
 
+static void iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+       lockdep_assert_held(&trans_pcie->reg_lock);
+
+       if (trans_pcie->ref_cmd_in_flight) {
+               trans_pcie->ref_cmd_in_flight = false;
+               IWL_DEBUG_RPM(trans, "clear ref_cmd_in_flight - unref\n");
+               iwl_trans_unref(trans);
+       }
+
+       if (!trans->cfg->base_params->apmg_wake_up_wa)
+               return;
+       if (WARN_ON(!trans_pcie->cmd_hold_nic_awake))
+               return;
+
+       trans_pcie->cmd_hold_nic_awake = false;
+       __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+                                  CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+}
+
 /*
  * iwl_pcie_txq_unmap -  Unmap any remaining DMA mappings and free skb's
  */
@@ -616,10 +639,24 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)
                        if (WARN_ON_ONCE(!skb))
                                continue;
 
-                       iwl_pcie_free_tso_page(skb);
+                       iwl_pcie_free_tso_page(trans_pcie, skb);
                }
                iwl_pcie_txq_free_tfd(trans, txq);
                q->read_ptr = iwl_queue_inc_wrap(q->read_ptr);
+
+               if (q->read_ptr == q->write_ptr) {
+                       unsigned long flags;
+
+                       spin_lock_irqsave(&trans_pcie->reg_lock, flags);
+                       if (txq_id != trans_pcie->cmd_queue) {
+                               IWL_DEBUG_RPM(trans, "Q %d - last tx freed\n",
+                                             q->id);
+                               iwl_trans_unref(trans);
+                       } else {
+                               iwl_pcie_clear_cmd_in_flight(trans);
+                       }
+                       spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
+               }
        }
        txq->active = false;
 
@@ -1047,7 +1084,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
                if (WARN_ON_ONCE(!skb))
                        continue;
 
-               iwl_pcie_free_tso_page(skb);
+               iwl_pcie_free_tso_page(trans_pcie, skb);
 
                __skb_queue_tail(skbs, skb);
 
@@ -1078,17 +1115,17 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
 
                while (!skb_queue_empty(&overflow_skbs)) {
                        struct sk_buff *skb = __skb_dequeue(&overflow_skbs);
-                       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-                       u8 dev_cmd_idx = IWL_TRANS_FIRST_DRIVER_DATA + 1;
-                       struct iwl_device_cmd *dev_cmd =
-                               info->driver_data[dev_cmd_idx];
+                       struct iwl_device_cmd *dev_cmd_ptr;
+
+                       dev_cmd_ptr = *(void **)((u8 *)skb->cb +
+                                                trans_pcie->dev_cmd_offs);
 
                        /*
                         * Note that we can very well be overflowing again.
                         * In that case, iwl_queue_space will be small again
                         * and we won't wake mac80211's queue.
                         */
-                       iwl_trans_pcie_tx(trans, skb, dev_cmd, txq_id);
+                       iwl_trans_pcie_tx(trans, skb, dev_cmd_ptr, txq_id);
                }
                spin_lock_bh(&txq->lock);
 
@@ -1098,7 +1135,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
 
        if (q->read_ptr == q->write_ptr) {
                IWL_DEBUG_RPM(trans, "Q %d - last tx reclaimed\n", q->id);
-               iwl_trans_pcie_unref(trans);
+               iwl_trans_unref(trans);
        }
 
 out:
@@ -1117,7 +1154,7 @@ static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans,
            !trans_pcie->ref_cmd_in_flight) {
                trans_pcie->ref_cmd_in_flight = true;
                IWL_DEBUG_RPM(trans, "set ref_cmd_in_flight - ref\n");
-               iwl_trans_pcie_ref(trans);
+               iwl_trans_ref(trans);
        }
 
        /*
@@ -1148,29 +1185,6 @@ static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans,
        return 0;
 }
 
-static int iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans)
-{
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
-       lockdep_assert_held(&trans_pcie->reg_lock);
-
-       if (trans_pcie->ref_cmd_in_flight) {
-               trans_pcie->ref_cmd_in_flight = false;
-               IWL_DEBUG_RPM(trans, "clear ref_cmd_in_flight - unref\n");
-               iwl_trans_pcie_unref(trans);
-       }
-
-       if (trans->cfg->base_params->apmg_wake_up_wa) {
-               if (WARN_ON(!trans_pcie->cmd_hold_nic_awake))
-                       return 0;
-
-               trans_pcie->cmd_hold_nic_awake = false;
-               __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
-                                          CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-       }
-       return 0;
-}
-
 /*
  * iwl_pcie_cmdq_reclaim - Reclaim TX command queue entries already Tx'd
  *
@@ -1340,6 +1354,15 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn,
        txq->active = true;
 }
 
+void iwl_trans_pcie_txq_set_shared_mode(struct iwl_trans *trans, u32 txq_id,
+                                       bool shared_mode)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       struct iwl_txq *txq = &trans_pcie->txq[txq_id];
+
+       txq->ampdu = !shared_mode;
+}
+
 void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id,
                                bool configure_scd)
 {
@@ -1581,9 +1604,9 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
 
        /* start the TFD with the scratchbuf */
        scratch_size = min_t(int, copy_size, IWL_HCMD_SCRATCHBUF_SIZE);
-       memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size);
+       memcpy(&txq->scratchbufs[idx], &out_cmd->hdr, scratch_size);
        iwl_pcie_txq_build_tfd(trans, txq,
-                              iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr),
+                              iwl_pcie_get_scratchbuf_dma(txq, idx),
                               scratch_size, true);
 
        /* map first command fragment, if any remains */
@@ -1786,6 +1809,16 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
        IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n",
                       iwl_get_cmd_string(trans, cmd->id));
 
+       if (pm_runtime_suspended(&trans_pcie->pci_dev->dev)) {
+               ret = wait_event_timeout(trans_pcie->d0i3_waitq,
+                                pm_runtime_active(&trans_pcie->pci_dev->dev),
+                                msecs_to_jiffies(IWL_TRANS_IDLE_TIMEOUT));
+               if (!ret) {
+                       IWL_ERR(trans, "Timeout exiting D0i3 before hcmd\n");
+                       return -ETIMEDOUT;
+               }
+       }
+
        cmd_idx = iwl_pcie_enqueue_hcmd(trans, cmd);
        if (cmd_idx < 0) {
                ret = cmd_idx;
@@ -1991,7 +2024,6 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb,
                                   struct iwl_cmd_meta *out_meta,
                                   struct iwl_device_cmd *dev_cmd, u16 tb1_len)
 {
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct iwl_trans_pcie *trans_pcie = txq->trans_pcie;
        struct ieee80211_hdr *hdr = (void *)skb->data;
        unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room;
@@ -2000,6 +2032,7 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb,
        u16 length, iv_len, amsdu_pad;
        u8 *start_hdr;
        struct iwl_tso_hdr_page *hdr_page;
+       struct page **page_ptr;
        int ret;
        struct tso_t tso;
 
@@ -2030,7 +2063,8 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb,
 
        get_page(hdr_page->page);
        start_hdr = hdr_page->pos;
-       info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA] = hdr_page->page;
+       page_ptr = (void *)((u8 *)skb->cb + trans_pcie->page_offs);
+       *page_ptr = hdr_page->page;
        memcpy(hdr_page->pos, skb->data + hdr_len, iv_len);
        hdr_page->pos += iv_len;
 
@@ -2197,6 +2231,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        __le16 fc;
        u8 hdr_len;
        u16 wifi_seq;
+       bool amsdu;
 
        txq = &trans_pcie->txq[txq_id];
        q = &txq->q;
@@ -2239,10 +2274,12 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
 
                /* don't put the packet on the ring, if there is no room */
                if (unlikely(iwl_queue_space(q) < 3)) {
-                       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+                       struct iwl_device_cmd **dev_cmd_ptr;
+
+                       dev_cmd_ptr = (void *)((u8 *)skb->cb +
+                                              trans_pcie->dev_cmd_offs);
 
-                       info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA + 1] =
-                               dev_cmd;
+                       *dev_cmd_ptr = dev_cmd;
                        __skb_queue_tail(&txq->overflow_q, skb);
 
                        spin_unlock(&txq->lock);
@@ -2288,11 +2325,18 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
         */
        len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) +
              hdr_len - IWL_HCMD_SCRATCHBUF_SIZE;
-       tb1_len = ALIGN(len, 4);
-
-       /* Tell NIC about any 2-byte padding after MAC header */
-       if (tb1_len != len)
-               tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK;
+       /* do not align A-MSDU to dword as the subframe header aligns it */
+       amsdu = ieee80211_is_data_qos(fc) &&
+               (*ieee80211_get_qos_ctl(hdr) &
+                IEEE80211_QOS_CTL_A_MSDU_PRESENT);
+       if (trans_pcie->sw_csum_tx || !amsdu) {
+               tb1_len = ALIGN(len, 4);
+               /* Tell NIC about any 2-byte padding after MAC header */
+               if (tb1_len != len)
+                       tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK;
+       } else {
+               tb1_len = len;
+       }
 
        /* The first TB points to the scratchbuf data - min_copy bytes */
        memcpy(&txq->scratchbufs[q->write_ptr], &dev_cmd->hdr,
@@ -2310,8 +2354,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
                goto out_err;
        iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false);
 
-       if (ieee80211_is_data_qos(fc) &&
-           (*ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_A_MSDU_PRESENT)) {
+       if (amsdu) {
                if (unlikely(iwl_fill_data_tbs_amsdu(trans, skb, txq, hdr_len,
                                                     out_meta, dev_cmd,
                                                     tb1_len)))
@@ -2342,7 +2385,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
                                txq->frozen_expiry_remainder = txq->wd_timeout;
                }
                IWL_DEBUG_RPM(trans, "Q: %d first tx - take ref\n", q->id);
-               iwl_trans_pcie_ref(trans);
+               iwl_trans_ref(trans);
        }
 
        /* Tell device the write index *just past* this latest filled TFD */