diff options
author | Tomas Winkler <tomas.winkler@intel.com> | 2008-06-11 21:46:56 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-06-14 12:18:06 -0400 |
commit | e26e47d94473af0c2a18beac664e526317b4f0b9 (patch) | |
tree | 034700425e4d2097c9a4b21a665429c95fab5fb1 /drivers/net/wireless/iwlwifi/iwl-5000.c | |
parent | 7f3e4bb60f81dd172d5e4b89220cb3f80c6dc552 (diff) |
iwlwifi: add TX aggregation code for 5000 HW
This patch adds TX aggregation handler for 5000 HW.
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Zhu Yi <yi.zhu@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-5000.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-5000.c | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 0ae5421c81c7..d3c0e10397c8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include "iwl-dev.h" | 41 | #include "iwl-dev.h" |
42 | #include "iwl-core.h" | 42 | #include "iwl-core.h" |
43 | #include "iwl-io.h" | 43 | #include "iwl-io.h" |
44 | #include "iwl-sta.h" | ||
44 | #include "iwl-helpers.h" | 45 | #include "iwl-helpers.h" |
45 | #include "iwl-5000-hw.h" | 46 | #include "iwl-5000-hw.h" |
46 | 47 | ||
@@ -976,6 +977,135 @@ static void iwl5000_txq_inval_byte_cnt_tbl(struct iwl_priv *priv, | |||
976 | } | 977 | } |
977 | } | 978 | } |
978 | 979 | ||
980 | static int iwl5000_tx_queue_set_q2ratid(struct iwl_priv *priv, u16 ra_tid, | ||
981 | u16 txq_id) | ||
982 | { | ||
983 | u32 tbl_dw_addr; | ||
984 | u32 tbl_dw; | ||
985 | u16 scd_q2ratid; | ||
986 | |||
987 | scd_q2ratid = ra_tid & IWL_SCD_QUEUE_RA_TID_MAP_RATID_MSK; | ||
988 | |||
989 | tbl_dw_addr = priv->scd_base_addr + | ||
990 | IWL50_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id); | ||
991 | |||
992 | tbl_dw = iwl_read_targ_mem(priv, tbl_dw_addr); | ||
993 | |||
994 | if (txq_id & 0x1) | ||
995 | tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF); | ||
996 | else | ||
997 | tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000); | ||
998 | |||
999 | iwl_write_targ_mem(priv, tbl_dw_addr, tbl_dw); | ||
1000 | |||
1001 | return 0; | ||
1002 | } | ||
1003 | static void iwl5000_tx_queue_stop_scheduler(struct iwl_priv *priv, u16 txq_id) | ||
1004 | { | ||
1005 | /* Simply stop the queue, but don't change any configuration; | ||
1006 | * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */ | ||
1007 | iwl_write_prph(priv, | ||
1008 | IWL50_SCD_QUEUE_STATUS_BITS(txq_id), | ||
1009 | (0 << IWL50_SCD_QUEUE_STTS_REG_POS_ACTIVE)| | ||
1010 | (1 << IWL50_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); | ||
1011 | } | ||
1012 | |||
1013 | static int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id, | ||
1014 | int tx_fifo, int sta_id, int tid, u16 ssn_idx) | ||
1015 | { | ||
1016 | unsigned long flags; | ||
1017 | int ret; | ||
1018 | u16 ra_tid; | ||
1019 | |||
1020 | if (IWL50_FIRST_AMPDU_QUEUE > txq_id) | ||
1021 | IWL_WARNING("queue number too small: %d, must be > %d\n", | ||
1022 | txq_id, IWL50_FIRST_AMPDU_QUEUE); | ||
1023 | |||
1024 | ra_tid = BUILD_RAxTID(sta_id, tid); | ||
1025 | |||
1026 | /* Modify device's station table to Tx this TID */ | ||
1027 | iwl_sta_modify_enable_tid_tx(priv, sta_id, tid); | ||
1028 | |||
1029 | spin_lock_irqsave(&priv->lock, flags); | ||
1030 | ret = iwl_grab_nic_access(priv); | ||
1031 | if (ret) { | ||
1032 | spin_unlock_irqrestore(&priv->lock, flags); | ||
1033 | return ret; | ||
1034 | } | ||
1035 | |||
1036 | /* Stop this Tx queue before configuring it */ | ||
1037 | iwl5000_tx_queue_stop_scheduler(priv, txq_id); | ||
1038 | |||
1039 | /* Map receiver-address / traffic-ID to this queue */ | ||
1040 | iwl5000_tx_queue_set_q2ratid(priv, ra_tid, txq_id); | ||
1041 | |||
1042 | /* Set this queue as a chain-building queue */ | ||
1043 | iwl_set_bits_prph(priv, IWL50_SCD_QUEUECHAIN_SEL, (1<<txq_id)); | ||
1044 | |||
1045 | /* enable aggregations for the queue */ | ||
1046 | iwl_set_bits_prph(priv, IWL50_SCD_AGGR_SEL, (1<<txq_id)); | ||
1047 | |||
1048 | /* Place first TFD at index corresponding to start sequence number. | ||
1049 | * Assumes that ssn_idx is valid (!= 0xFFF) */ | ||
1050 | priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); | ||
1051 | priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); | ||
1052 | iwl5000_set_wr_ptrs(priv, txq_id, ssn_idx); | ||
1053 | |||
1054 | /* Set up Tx window size and frame limit for this queue */ | ||
1055 | iwl_write_targ_mem(priv, priv->scd_base_addr + | ||
1056 | IWL50_SCD_CONTEXT_QUEUE_OFFSET(txq_id) + | ||
1057 | sizeof(u32), | ||
1058 | ((SCD_WIN_SIZE << | ||
1059 | IWL50_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & | ||
1060 | IWL50_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | | ||
1061 | ((SCD_FRAME_LIMIT << | ||
1062 | IWL50_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & | ||
1063 | IWL50_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); | ||
1064 | |||
1065 | iwl_set_bits_prph(priv, IWL50_SCD_INTERRUPT_MASK, (1 << txq_id)); | ||
1066 | |||
1067 | /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ | ||
1068 | iwl5000_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1); | ||
1069 | |||
1070 | iwl_release_nic_access(priv); | ||
1071 | spin_unlock_irqrestore(&priv->lock, flags); | ||
1072 | |||
1073 | return 0; | ||
1074 | } | ||
1075 | |||
1076 | static int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, | ||
1077 | u16 ssn_idx, u8 tx_fifo) | ||
1078 | { | ||
1079 | int ret; | ||
1080 | |||
1081 | if (IWL50_FIRST_AMPDU_QUEUE > txq_id) { | ||
1082 | IWL_WARNING("queue number too small: %d, must be > %d\n", | ||
1083 | txq_id, IWL50_FIRST_AMPDU_QUEUE); | ||
1084 | return -EINVAL; | ||
1085 | } | ||
1086 | |||
1087 | ret = iwl_grab_nic_access(priv); | ||
1088 | if (ret) | ||
1089 | return ret; | ||
1090 | |||
1091 | iwl5000_tx_queue_stop_scheduler(priv, txq_id); | ||
1092 | |||
1093 | iwl_clear_bits_prph(priv, IWL50_SCD_AGGR_SEL, (1 << txq_id)); | ||
1094 | |||
1095 | priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); | ||
1096 | priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); | ||
1097 | /* supposes that ssn_idx is valid (!= 0xFFF) */ | ||
1098 | iwl5000_set_wr_ptrs(priv, txq_id, ssn_idx); | ||
1099 | |||
1100 | iwl_clear_bits_prph(priv, IWL50_SCD_INTERRUPT_MASK, (1 << txq_id)); | ||
1101 | iwl_txq_ctx_deactivate(priv, txq_id); | ||
1102 | iwl5000_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0); | ||
1103 | |||
1104 | iwl_release_nic_access(priv); | ||
1105 | |||
1106 | return 0; | ||
1107 | } | ||
1108 | |||
979 | static u16 iwl5000_build_addsta_hcmd(const struct iwl_addsta_cmd *cmd, u8 *data) | 1109 | static u16 iwl5000_build_addsta_hcmd(const struct iwl_addsta_cmd *cmd, u8 *data) |
980 | { | 1110 | { |
981 | u16 size = (u16)sizeof(struct iwl_addsta_cmd); | 1111 | u16 size = (u16)sizeof(struct iwl_addsta_cmd); |
@@ -1319,6 +1449,8 @@ static struct iwl_lib_ops iwl5000_lib = { | |||
1319 | .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl, | 1449 | .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl, |
1320 | .txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl, | 1450 | .txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl, |
1321 | .txq_set_sched = iwl5000_txq_set_sched, | 1451 | .txq_set_sched = iwl5000_txq_set_sched, |
1452 | .txq_agg_enable = iwl5000_txq_agg_enable, | ||
1453 | .txq_agg_disable = iwl5000_txq_agg_disable, | ||
1322 | .rx_handler_setup = iwl5000_rx_handler_setup, | 1454 | .rx_handler_setup = iwl5000_rx_handler_setup, |
1323 | .setup_deferred_work = iwl5000_setup_deferred_work, | 1455 | .setup_deferred_work = iwl5000_setup_deferred_work, |
1324 | .is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr, | 1456 | .is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr, |