|
@@ -369,21 +369,13 @@ error:
|
|
|
}
|
|
|
|
|
|
static int iwl_trans_txq_init(struct iwl_trans *trans, struct iwl_tx_queue *txq,
|
|
|
- int slots_num, u32 txq_id)
|
|
|
+ int slots_num, u32 txq_id)
|
|
|
{
|
|
|
int ret;
|
|
|
|
|
|
txq->need_update = 0;
|
|
|
memset(txq->meta, 0, sizeof(txq->meta[0]) * slots_num);
|
|
|
|
|
|
- /*
|
|
|
- * For the default queues 0-3, set up the swq_id
|
|
|
- * already -- all others need to get one later
|
|
|
- * (if they need one at all).
|
|
|
- */
|
|
|
- if (txq_id < 4)
|
|
|
- iwl_set_swq_id(txq, txq_id, txq_id);
|
|
|
-
|
|
|
/* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
|
|
|
* iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
|
|
|
BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
|
|
@@ -894,59 +886,6 @@ static int iwl_prepare_card_hw(struct iwl_trans *trans)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-#define IWL_AC_UNSET -1
|
|
|
-
|
|
|
-struct queue_to_fifo_ac {
|
|
|
- s8 fifo, ac;
|
|
|
-};
|
|
|
-
|
|
|
-static const struct queue_to_fifo_ac iwlagn_default_queue_to_tx_fifo[] = {
|
|
|
- { IWL_TX_FIFO_VO, IEEE80211_AC_VO, },
|
|
|
- { IWL_TX_FIFO_VI, IEEE80211_AC_VI, },
|
|
|
- { IWL_TX_FIFO_BE, IEEE80211_AC_BE, },
|
|
|
- { IWL_TX_FIFO_BK, IEEE80211_AC_BK, },
|
|
|
- { IWLAGN_CMD_FIFO_NUM, IWL_AC_UNSET, },
|
|
|
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
|
|
|
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
|
|
|
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
|
|
|
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
|
|
|
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
|
|
|
- { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, },
|
|
|
-};
|
|
|
-
|
|
|
-static const struct queue_to_fifo_ac iwlagn_ipan_queue_to_tx_fifo[] = {
|
|
|
- { IWL_TX_FIFO_VO, IEEE80211_AC_VO, },
|
|
|
- { IWL_TX_FIFO_VI, IEEE80211_AC_VI, },
|
|
|
- { IWL_TX_FIFO_BE, IEEE80211_AC_BE, },
|
|
|
- { IWL_TX_FIFO_BK, IEEE80211_AC_BK, },
|
|
|
- { IWL_TX_FIFO_BK_IPAN, IEEE80211_AC_BK, },
|
|
|
- { IWL_TX_FIFO_BE_IPAN, IEEE80211_AC_BE, },
|
|
|
- { IWL_TX_FIFO_VI_IPAN, IEEE80211_AC_VI, },
|
|
|
- { IWL_TX_FIFO_VO_IPAN, IEEE80211_AC_VO, },
|
|
|
- { IWL_TX_FIFO_BE_IPAN, 2, },
|
|
|
- { IWLAGN_CMD_FIFO_NUM, IWL_AC_UNSET, },
|
|
|
- { IWL_TX_FIFO_AUX, IWL_AC_UNSET, },
|
|
|
-};
|
|
|
-
|
|
|
-static const u8 iwlagn_bss_ac_to_fifo[] = {
|
|
|
- IWL_TX_FIFO_VO,
|
|
|
- IWL_TX_FIFO_VI,
|
|
|
- IWL_TX_FIFO_BE,
|
|
|
- IWL_TX_FIFO_BK,
|
|
|
-};
|
|
|
-static const u8 iwlagn_bss_ac_to_queue[] = {
|
|
|
- 0, 1, 2, 3,
|
|
|
-};
|
|
|
-static const u8 iwlagn_pan_ac_to_fifo[] = {
|
|
|
- IWL_TX_FIFO_VO_IPAN,
|
|
|
- IWL_TX_FIFO_VI_IPAN,
|
|
|
- IWL_TX_FIFO_BE_IPAN,
|
|
|
- IWL_TX_FIFO_BK_IPAN,
|
|
|
-};
|
|
|
-static const u8 iwlagn_pan_ac_to_queue[] = {
|
|
|
- 7, 6, 5, 4,
|
|
|
-};
|
|
|
-
|
|
|
/*
|
|
|
* ucode
|
|
|
*/
|
|
@@ -1027,19 +966,8 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
|
|
|
const struct fw_img *fw)
|
|
|
{
|
|
|
int ret;
|
|
|
- struct iwl_trans_pcie *trans_pcie =
|
|
|
- IWL_TRANS_GET_PCIE_TRANS(trans);
|
|
|
bool hw_rfkill;
|
|
|
|
|
|
- trans_pcie->ac_to_queue[IWL_RXON_CTX_BSS] = iwlagn_bss_ac_to_queue;
|
|
|
- trans_pcie->ac_to_queue[IWL_RXON_CTX_PAN] = iwlagn_pan_ac_to_queue;
|
|
|
-
|
|
|
- trans_pcie->ac_to_fifo[IWL_RXON_CTX_BSS] = iwlagn_bss_ac_to_fifo;
|
|
|
- trans_pcie->ac_to_fifo[IWL_RXON_CTX_PAN] = iwlagn_pan_ac_to_fifo;
|
|
|
-
|
|
|
- trans_pcie->mcast_queue[IWL_RXON_CTX_BSS] = 0;
|
|
|
- trans_pcie->mcast_queue[IWL_RXON_CTX_PAN] = IWL_IPAN_MCAST_QUEUE;
|
|
|
-
|
|
|
/* This may fail if AMT took ownership of the device */
|
|
|
if (iwl_prepare_card_hw(trans)) {
|
|
|
IWL_WARN(trans, "Exit HW not ready\n");
|
|
@@ -1097,9 +1025,7 @@ static void iwl_trans_txq_set_sched(struct iwl_trans *trans, u32 mask)
|
|
|
|
|
|
static void iwl_tx_start(struct iwl_trans *trans)
|
|
|
{
|
|
|
- const struct queue_to_fifo_ac *queue_to_fifo;
|
|
|
- struct iwl_trans_pcie *trans_pcie =
|
|
|
- IWL_TRANS_GET_PCIE_TRANS(trans);
|
|
|
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
|
|
u32 a;
|
|
|
unsigned long flags;
|
|
|
int i, chan;
|
|
@@ -1165,41 +1091,19 @@ static void iwl_tx_start(struct iwl_trans *trans)
|
|
|
/* Activate all Tx DMA/FIFO channels */
|
|
|
iwl_trans_txq_set_sched(trans, IWL_MASK(0, 7));
|
|
|
|
|
|
- /* map queues to FIFOs */
|
|
|
- if (trans->shrd->valid_contexts != BIT(IWL_RXON_CTX_BSS))
|
|
|
- queue_to_fifo = iwlagn_ipan_queue_to_tx_fifo;
|
|
|
- else
|
|
|
- queue_to_fifo = iwlagn_default_queue_to_tx_fifo;
|
|
|
-
|
|
|
iwl_trans_set_wr_ptrs(trans, trans_pcie->cmd_queue, 0);
|
|
|
|
|
|
- /* make sure all queue are not stopped */
|
|
|
- memset(&trans_pcie->queue_stopped[0], 0,
|
|
|
- sizeof(trans_pcie->queue_stopped));
|
|
|
- for (i = 0; i < 4; i++)
|
|
|
- atomic_set(&trans_pcie->queue_stop_count[i], 0);
|
|
|
-
|
|
|
- /* reset to 0 to enable all the queue first */
|
|
|
- trans_pcie->txq_ctx_active_msk = 0;
|
|
|
+ /* make sure all queue are not stopped/used */
|
|
|
+ memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped));
|
|
|
+ memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used));
|
|
|
|
|
|
- BUILD_BUG_ON(ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo) <
|
|
|
- IWLAGN_FIRST_AMPDU_QUEUE);
|
|
|
- BUILD_BUG_ON(ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo) <
|
|
|
- IWLAGN_FIRST_AMPDU_QUEUE);
|
|
|
+ for (i = 0; i < trans_pcie->n_q_to_fifo; i++) {
|
|
|
+ int fifo = trans_pcie->setup_q_to_fifo[i];
|
|
|
|
|
|
- for (i = 0; i < IWLAGN_FIRST_AMPDU_QUEUE; i++) {
|
|
|
- int fifo = queue_to_fifo[i].fifo;
|
|
|
- int ac = queue_to_fifo[i].ac;
|
|
|
+ set_bit(i, trans_pcie->queue_used);
|
|
|
|
|
|
- iwl_txq_ctx_activate(trans_pcie, i);
|
|
|
-
|
|
|
- if (fifo == IWL_TX_FIFO_UNUSED)
|
|
|
- continue;
|
|
|
-
|
|
|
- if (ac != IWL_AC_UNSET)
|
|
|
- iwl_set_swq_id(&trans_pcie->txq[i], ac, i);
|
|
|
iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[i],
|
|
|
- fifo, 0);
|
|
|
+ fifo, true);
|
|
|
}
|
|
|
|
|
|
spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
|
|
@@ -1324,70 +1228,32 @@ static void iwl_trans_pcie_wowlan_suspend(struct iwl_trans *trans)
|
|
|
}
|
|
|
|
|
|
static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
|
|
|
- struct iwl_device_cmd *dev_cmd, enum iwl_rxon_context_id ctx,
|
|
|
- u8 sta_id, u8 tid)
|
|
|
+ struct iwl_device_cmd *dev_cmd, int txq_id)
|
|
|
{
|
|
|
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
|
|
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
|
|
struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload;
|
|
|
struct iwl_cmd_meta *out_meta;
|
|
|
struct iwl_tx_queue *txq;
|
|
|
struct iwl_queue *q;
|
|
|
-
|
|
|
dma_addr_t phys_addr = 0;
|
|
|
dma_addr_t txcmd_phys;
|
|
|
dma_addr_t scratch_phys;
|
|
|
u16 len, firstlen, secondlen;
|
|
|
u8 wait_write_ptr = 0;
|
|
|
- u8 txq_id;
|
|
|
- bool is_agg = false;
|
|
|
__le16 fc = hdr->frame_control;
|
|
|
u8 hdr_len = ieee80211_hdrlen(fc);
|
|
|
u16 __maybe_unused wifi_seq;
|
|
|
|
|
|
- /*
|
|
|
- * Send this frame after DTIM -- there's a special queue
|
|
|
- * reserved for this for contexts that support AP mode.
|
|
|
- */
|
|
|
- if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
|
|
|
- txq_id = trans_pcie->mcast_queue[ctx];
|
|
|
-
|
|
|
- /*
|
|
|
- * The microcode will clear the more data
|
|
|
- * bit in the last frame it transmits.
|
|
|
- */
|
|
|
- hdr->frame_control |=
|
|
|
- cpu_to_le16(IEEE80211_FCTL_MOREDATA);
|
|
|
- } else if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
|
|
|
- txq_id = IWL_AUX_QUEUE;
|
|
|
- else
|
|
|
- txq_id =
|
|
|
- trans_pcie->ac_to_queue[ctx][skb_get_queue_mapping(skb)];
|
|
|
-
|
|
|
- /* aggregation is on for this <sta,tid> */
|
|
|
- if (info->flags & IEEE80211_TX_CTL_AMPDU) {
|
|
|
- WARN_ON(tid >= IWL_MAX_TID_COUNT);
|
|
|
- txq_id = trans_pcie->agg_txq[sta_id][tid];
|
|
|
- is_agg = true;
|
|
|
- }
|
|
|
-
|
|
|
txq = &trans_pcie->txq[txq_id];
|
|
|
q = &txq->q;
|
|
|
|
|
|
- spin_lock(&txq->lock);
|
|
|
+ if (unlikely(!test_bit(txq_id, trans_pcie->queue_used))) {
|
|
|
+ WARN_ON_ONCE(1);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
|
|
|
- /* In AGG mode, the index in the ring must correspond to the WiFi
|
|
|
- * sequence number. This is a HW requirements to help the SCD to parse
|
|
|
- * the BA.
|
|
|
- * Check here that the packets are in the right place on the ring.
|
|
|
- */
|
|
|
-#ifdef CONFIG_IWLWIFI_DEBUG
|
|
|
- wifi_seq = SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
|
|
|
- WARN_ONCE(is_agg && ((wifi_seq & 0xff) != q->write_ptr),
|
|
|
- "Q: %d WiFi Seq %d tfdNum %d",
|
|
|
- txq_id, wifi_seq, q->write_ptr);
|
|
|
-#endif
|
|
|
+ spin_lock(&txq->lock);
|
|
|
|
|
|
/* Set up driver data for this TFD */
|
|
|
txq->skbs[q->write_ptr] = skb;
|
|
@@ -1564,8 +1430,8 @@ static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans)
|
|
|
iwl_enable_rfkill_int(trans);
|
|
|
}
|
|
|
|
|
|
-static int iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid,
|
|
|
- int txq_id, int ssn, struct sk_buff_head *skbs)
|
|
|
+static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
|
|
|
+ struct sk_buff_head *skbs)
|
|
|
{
|
|
|
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
|
|
struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id];
|
|
@@ -1577,33 +1443,15 @@ static int iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid,
|
|
|
|
|
|
txq->time_stamp = jiffies;
|
|
|
|
|
|
- if (unlikely(txq_id >= IWLAGN_FIRST_AMPDU_QUEUE &&
|
|
|
- tid != IWL_TID_NON_QOS &&
|
|
|
- txq_id != trans_pcie->agg_txq[sta_id][tid])) {
|
|
|
- /*
|
|
|
- * FIXME: this is a uCode bug which need to be addressed,
|
|
|
- * log the information and return for now.
|
|
|
- * Since it is can possibly happen very often and in order
|
|
|
- * not to fill the syslog, don't use IWL_ERR or IWL_WARN
|
|
|
- */
|
|
|
- IWL_DEBUG_TX_QUEUES(trans, "Bad queue mapping txq_id %d, "
|
|
|
- "agg_txq[sta_id[tid] %d", txq_id,
|
|
|
- trans_pcie->agg_txq[sta_id][tid]);
|
|
|
- spin_unlock(&txq->lock);
|
|
|
- return 1;
|
|
|
- }
|
|
|
-
|
|
|
if (txq->q.read_ptr != tfd_num) {
|
|
|
- IWL_DEBUG_TX_REPLY(trans, "[Q %d | AC %d] %d -> %d (%d)\n",
|
|
|
- txq_id, iwl_get_queue_ac(txq), txq->q.read_ptr,
|
|
|
- tfd_num, ssn);
|
|
|
+ IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d -> %d (%d)\n",
|
|
|
+ txq_id, txq->q.read_ptr, tfd_num, ssn);
|
|
|
freed = iwl_tx_queue_reclaim(trans, txq_id, tfd_num, skbs);
|
|
|
if (iwl_queue_space(&txq->q) > txq->q.low_mark)
|
|
|
iwl_wake_queue(trans, txq);
|
|
|
}
|
|
|
|
|
|
spin_unlock(&txq->lock);
|
|
|
- return 0;
|
|
|
}
|
|
|
|
|
|
static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val)
|
|
@@ -1622,7 +1470,7 @@ static u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs)
|
|
|
}
|
|
|
|
|
|
static void iwl_trans_pcie_configure(struct iwl_trans *trans,
|
|
|
- const struct iwl_trans_config *trans_cfg)
|
|
|
+ const struct iwl_trans_config *trans_cfg)
|
|
|
{
|
|
|
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
|
|
|
|
@@ -1634,6 +1482,17 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans,
|
|
|
if (trans_pcie->n_no_reclaim_cmds)
|
|
|
memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds,
|
|
|
trans_pcie->n_no_reclaim_cmds * sizeof(u8));
|
|
|
+
|
|
|
+ trans_pcie->n_q_to_fifo = trans_cfg->n_queue_to_fifo;
|
|
|
+
|
|
|
+ if (WARN_ON(trans_pcie->n_q_to_fifo > IWL_MAX_HW_QUEUES))
|
|
|
+ trans_pcie->n_q_to_fifo = IWL_MAX_HW_QUEUES;
|
|
|
+
|
|
|
+ /* at least the command queue must be mapped */
|
|
|
+ WARN_ON(!trans_pcie->n_q_to_fifo);
|
|
|
+
|
|
|
+ memcpy(trans_pcie->setup_q_to_fifo, trans_cfg->queue_to_fifo,
|
|
|
+ trans_pcie->n_q_to_fifo * sizeof(u8));
|
|
|
}
|
|
|
|
|
|
static void iwl_trans_pcie_free(struct iwl_trans *trans)
|
|
@@ -1957,18 +1816,10 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file,
|
|
|
txq = &trans_pcie->txq[cnt];
|
|
|
q = &txq->q;
|
|
|
pos += scnprintf(buf + pos, bufsz - pos,
|
|
|
- "hwq %.2d: read=%u write=%u stop=%d"
|
|
|
- " swq_id=%#.2x (ac %d/hwq %d)\n",
|
|
|
+ "hwq %.2d: read=%u write=%u use=%d stop=%d\n",
|
|
|
cnt, q->read_ptr, q->write_ptr,
|
|
|
- !!test_bit(cnt, trans_pcie->queue_stopped),
|
|
|
- txq->swq_id, txq->swq_id & 3,
|
|
|
- (txq->swq_id >> 2) & 0x1f);
|
|
|
- if (cnt >= 4)
|
|
|
- continue;
|
|
|
- /* for the ACs, display the stop count too */
|
|
|
- pos += scnprintf(buf + pos, bufsz - pos,
|
|
|
- " stop-count: %d\n",
|
|
|
- atomic_read(&trans_pcie->queue_stop_count[cnt]));
|
|
|
+ !!test_bit(cnt, trans_pcie->queue_used),
|
|
|
+ !!test_bit(cnt, trans_pcie->queue_stopped));
|
|
|
}
|
|
|
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
|
|
|
kfree(buf);
|
|
@@ -2210,7 +2061,6 @@ const struct iwl_trans_ops trans_ops_pcie = {
|
|
|
.reclaim = iwl_trans_pcie_reclaim,
|
|
|
|
|
|
.tx_agg_disable = iwl_trans_pcie_tx_agg_disable,
|
|
|
- .tx_agg_alloc = iwl_trans_pcie_tx_agg_alloc,
|
|
|
.tx_agg_setup = iwl_trans_pcie_tx_agg_setup,
|
|
|
|
|
|
.free = iwl_trans_pcie_free,
|