iwl-agn-tx.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /******************************************************************************
  2. *
  3. * GPL LICENSE SUMMARY
  4. *
  5. * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved.
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of version 2 of the GNU General Public License as
  9. * published by the Free Software Foundation.
  10. *
  11. * This program is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
  19. * USA
  20. *
  21. * The full GNU General Public License is included in this distribution
  22. * in the file called LICENSE.GPL.
  23. *
  24. * Contact Information:
  25. * Intel Linux Wireless <ilw@linux.intel.com>
  26. * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  27. *
  28. *****************************************************************************/
  29. #include <linux/kernel.h>
  30. #include <linux/module.h>
  31. #include <linux/init.h>
  32. #include <linux/sched.h>
  33. #include "iwl-dev.h"
  34. #include "iwl-core.h"
  35. #include "iwl-sta.h"
  36. #include "iwl-io.h"
  37. #include "iwl-5000-hw.h"
  38. /**
  39. * iwlagn_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array
  40. */
  41. void iwlagn_txq_update_byte_cnt_tbl(struct iwl_priv *priv,
  42. struct iwl_tx_queue *txq,
  43. u16 byte_cnt)
  44. {
  45. struct iwl5000_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr;
  46. int write_ptr = txq->q.write_ptr;
  47. int txq_id = txq->q.id;
  48. u8 sec_ctl = 0;
  49. u8 sta_id = 0;
  50. u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
  51. __le16 bc_ent;
  52. WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX);
  53. if (txq_id != IWL_CMD_QUEUE_NUM) {
  54. sta_id = txq->cmd[txq->q.write_ptr]->cmd.tx.sta_id;
  55. sec_ctl = txq->cmd[txq->q.write_ptr]->cmd.tx.sec_ctl;
  56. switch (sec_ctl & TX_CMD_SEC_MSK) {
  57. case TX_CMD_SEC_CCM:
  58. len += CCMP_MIC_LEN;
  59. break;
  60. case TX_CMD_SEC_TKIP:
  61. len += TKIP_ICV_LEN;
  62. break;
  63. case TX_CMD_SEC_WEP:
  64. len += WEP_IV_LEN + WEP_ICV_LEN;
  65. break;
  66. }
  67. }
  68. bc_ent = cpu_to_le16((len & 0xFFF) | (sta_id << 12));
  69. scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent;
  70. if (write_ptr < TFD_QUEUE_SIZE_BC_DUP)
  71. scd_bc_tbl[txq_id].
  72. tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent;
  73. }
  74. void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_priv *priv,
  75. struct iwl_tx_queue *txq)
  76. {
  77. struct iwl5000_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr;
  78. int txq_id = txq->q.id;
  79. int read_ptr = txq->q.read_ptr;
  80. u8 sta_id = 0;
  81. __le16 bc_ent;
  82. WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX);
  83. if (txq_id != IWL_CMD_QUEUE_NUM)
  84. sta_id = txq->cmd[read_ptr]->cmd.tx.sta_id;
  85. bc_ent = cpu_to_le16(1 | (sta_id << 12));
  86. scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent;
  87. if (read_ptr < TFD_QUEUE_SIZE_BC_DUP)
  88. scd_bc_tbl[txq_id].
  89. tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent;
  90. }
  91. static int iwlagn_tx_queue_set_q2ratid(struct iwl_priv *priv, u16 ra_tid,
  92. u16 txq_id)
  93. {
  94. u32 tbl_dw_addr;
  95. u32 tbl_dw;
  96. u16 scd_q2ratid;
  97. scd_q2ratid = ra_tid & IWL_SCD_QUEUE_RA_TID_MAP_RATID_MSK;
  98. tbl_dw_addr = priv->scd_base_addr +
  99. IWL50_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id);
  100. tbl_dw = iwl_read_targ_mem(priv, tbl_dw_addr);
  101. if (txq_id & 0x1)
  102. tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF);
  103. else
  104. tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000);
  105. iwl_write_targ_mem(priv, tbl_dw_addr, tbl_dw);
  106. return 0;
  107. }
  108. static void iwlagn_tx_queue_stop_scheduler(struct iwl_priv *priv, u16 txq_id)
  109. {
  110. /* Simply stop the queue, but don't change any configuration;
  111. * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */
  112. iwl_write_prph(priv,
  113. IWL50_SCD_QUEUE_STATUS_BITS(txq_id),
  114. (0 << IWL50_SCD_QUEUE_STTS_REG_POS_ACTIVE)|
  115. (1 << IWL50_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
  116. }
  117. void iwlagn_set_wr_ptrs(struct iwl_priv *priv,
  118. int txq_id, u32 index)
  119. {
  120. iwl_write_direct32(priv, HBUS_TARG_WRPTR,
  121. (index & 0xff) | (txq_id << 8));
  122. iwl_write_prph(priv, IWL50_SCD_QUEUE_RDPTR(txq_id), index);
  123. }
  124. void iwlagn_tx_queue_set_status(struct iwl_priv *priv,
  125. struct iwl_tx_queue *txq,
  126. int tx_fifo_id, int scd_retry)
  127. {
  128. int txq_id = txq->q.id;
  129. int active = test_bit(txq_id, &priv->txq_ctx_active_msk) ? 1 : 0;
  130. iwl_write_prph(priv, IWL50_SCD_QUEUE_STATUS_BITS(txq_id),
  131. (active << IWL50_SCD_QUEUE_STTS_REG_POS_ACTIVE) |
  132. (tx_fifo_id << IWL50_SCD_QUEUE_STTS_REG_POS_TXF) |
  133. (1 << IWL50_SCD_QUEUE_STTS_REG_POS_WSL) |
  134. IWL50_SCD_QUEUE_STTS_REG_MSK);
  135. txq->sched_retry = scd_retry;
  136. IWL_DEBUG_INFO(priv, "%s %s Queue %d on FIFO %d\n",
  137. active ? "Activate" : "Deactivate",
  138. scd_retry ? "BA" : "AC/CMD", txq_id, tx_fifo_id);
  139. }
  140. int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id,
  141. int tx_fifo, int sta_id, int tid, u16 ssn_idx)
  142. {
  143. unsigned long flags;
  144. u16 ra_tid;
  145. if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) ||
  146. (IWL50_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues
  147. <= txq_id)) {
  148. IWL_WARN(priv,
  149. "queue number out of range: %d, must be %d to %d\n",
  150. txq_id, IWL50_FIRST_AMPDU_QUEUE,
  151. IWL50_FIRST_AMPDU_QUEUE +
  152. priv->cfg->num_of_ampdu_queues - 1);
  153. return -EINVAL;
  154. }
  155. ra_tid = BUILD_RAxTID(sta_id, tid);
  156. /* Modify device's station table to Tx this TID */
  157. iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
  158. spin_lock_irqsave(&priv->lock, flags);
  159. /* Stop this Tx queue before configuring it */
  160. iwlagn_tx_queue_stop_scheduler(priv, txq_id);
  161. /* Map receiver-address / traffic-ID to this queue */
  162. iwlagn_tx_queue_set_q2ratid(priv, ra_tid, txq_id);
  163. /* Set this queue as a chain-building queue */
  164. iwl_set_bits_prph(priv, IWL50_SCD_QUEUECHAIN_SEL, (1<<txq_id));
  165. /* enable aggregations for the queue */
  166. iwl_set_bits_prph(priv, IWL50_SCD_AGGR_SEL, (1<<txq_id));
  167. /* Place first TFD at index corresponding to start sequence number.
  168. * Assumes that ssn_idx is valid (!= 0xFFF) */
  169. priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
  170. priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
  171. iwlagn_set_wr_ptrs(priv, txq_id, ssn_idx);
  172. /* Set up Tx window size and frame limit for this queue */
  173. iwl_write_targ_mem(priv, priv->scd_base_addr +
  174. IWL50_SCD_CONTEXT_QUEUE_OFFSET(txq_id) +
  175. sizeof(u32),
  176. ((SCD_WIN_SIZE <<
  177. IWL50_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
  178. IWL50_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
  179. ((SCD_FRAME_LIMIT <<
  180. IWL50_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
  181. IWL50_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
  182. iwl_set_bits_prph(priv, IWL50_SCD_INTERRUPT_MASK, (1 << txq_id));
  183. /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
  184. iwlagn_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1);
  185. spin_unlock_irqrestore(&priv->lock, flags);
  186. return 0;
  187. }
  188. int iwlagn_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
  189. u16 ssn_idx, u8 tx_fifo)
  190. {
  191. if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) ||
  192. (IWL50_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues
  193. <= txq_id)) {
  194. IWL_ERR(priv,
  195. "queue number out of range: %d, must be %d to %d\n",
  196. txq_id, IWL50_FIRST_AMPDU_QUEUE,
  197. IWL50_FIRST_AMPDU_QUEUE +
  198. priv->cfg->num_of_ampdu_queues - 1);
  199. return -EINVAL;
  200. }
  201. iwlagn_tx_queue_stop_scheduler(priv, txq_id);
  202. iwl_clear_bits_prph(priv, IWL50_SCD_AGGR_SEL, (1 << txq_id));
  203. priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
  204. priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
  205. /* supposes that ssn_idx is valid (!= 0xFFF) */
  206. iwlagn_set_wr_ptrs(priv, txq_id, ssn_idx);
  207. iwl_clear_bits_prph(priv, IWL50_SCD_INTERRUPT_MASK, (1 << txq_id));
  208. iwl_txq_ctx_deactivate(priv, txq_id);
  209. iwlagn_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
  210. return 0;
  211. }
  212. /*
  213. * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask
  214. * must be called under priv->lock and mac access
  215. */
  216. void iwlagn_txq_set_sched(struct iwl_priv *priv, u32 mask)
  217. {
  218. iwl_write_prph(priv, IWL50_SCD_TXFACT, mask);
  219. }