aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorEytan Lifshitz <eytan.lifshitz@intel.com>2013-05-19 12:14:41 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-05-29 03:03:18 -0400
commit9ee718aa9269cf56040cf12f0f6ac6e0057397b2 (patch)
tree5d4b9d8bb7b16f758ea0d6c8a4c85e87f4681600 /drivers
parentf68d18f202d50f60746a8bcce1dc965b2f5035c0 (diff)
iwlwifi: mvm: add thermal throttling and CT kill
In order to avoid NIC destruction due to high temperature, CT kill will power down the NIC. To avoid this, thermal throttling will decrease throughput to prevent the NIC from reaching the temperature at which CT kill is performed. Signed-off-by: Eytan Lifshitz <eytan.lifshitz@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-csr.h19
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-prph.h12
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/Makefile2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/bt-coex.c6
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api.h211
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw.c2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c5
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c4
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h101
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c18
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rs.c26
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rs.h5
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rx.c22
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.c13
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.h6
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tt.c509
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/utils.c31
17 files changed, 974 insertions, 18 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h
index 20e845d4da04..a276af476e2d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-csr.h
+++ b/drivers/net/wireless/iwlwifi/iwl-csr.h
@@ -472,4 +472,23 @@
472#define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10) 472#define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10)
473#define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0) 473#define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0)
474 474
475/*****************************************************************************
476 * 7000/3000 series SHR DTS addresses *
477 *****************************************************************************/
478
479/* Diode Results Register Structure: */
480enum dtd_diode_reg {
481 DTS_DIODE_REG_DIG_VAL = 0x000000FF, /* bits [7:0] */
482 DTS_DIODE_REG_VREF_LOW = 0x0000FF00, /* bits [15:8] */
483 DTS_DIODE_REG_VREF_HIGH = 0x00FF0000, /* bits [23:16] */
484 DTS_DIODE_REG_VREF_ID = 0x03000000, /* bits [25:24] */
485 DTS_DIODE_REG_PASS_ONCE = 0x80000000, /* bits [31:31] */
486 DTS_DIODE_REG_FLAGS_MSK = 0xFF000000, /* bits [31:24] */
487/* Those are the masks INSIDE the flags bit-field: */
488 DTS_DIODE_REG_FLAGS_VREFS_ID_POS = 0,
489 DTS_DIODE_REG_FLAGS_VREFS_ID = 0x00000003, /* bits [1:0] */
490 DTS_DIODE_REG_FLAGS_PASS_ONCE_POS = 7,
491 DTS_DIODE_REG_FLAGS_PASS_ONCE = 0x00000080, /* bits [7:7] */
492};
493
475#endif /* !__iwl_csr_h__ */ 494#endif /* !__iwl_csr_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h
index 386f2a7c87cb..ff8cc75c189d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/iwlwifi/iwl-prph.h
@@ -100,6 +100,18 @@
100/* Device system time */ 100/* Device system time */
101#define DEVICE_SYSTEM_TIME_REG 0xA0206C 101#define DEVICE_SYSTEM_TIME_REG 0xA0206C
102 102
103/*****************************************************************************
104 * 7000/3000 series SHR DTS addresses *
105 *****************************************************************************/
106
107#define SHR_MISC_WFM_DTS_EN (0x00a10024)
108#define DTSC_CFG_MODE (0x00a10604)
109#define DTSC_VREF_AVG (0x00a10648)
110#define DTSC_VREF5_AVG (0x00a1064c)
111#define DTSC_CFG_MODE_PERIODIC (0x2)
112#define DTSC_PTAT_AVG (0x00a10650)
113
114
103/** 115/**
104 * Tx Scheduler 116 * Tx Scheduler
105 * 117 *
diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile
index 2acc44b40986..ff856e543ae8 100644
--- a/drivers/net/wireless/iwlwifi/mvm/Makefile
+++ b/drivers/net/wireless/iwlwifi/mvm/Makefile
@@ -3,7 +3,7 @@ iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
3iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o 3iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o
4iwlmvm-y += scan.o time-event.o rs.o 4iwlmvm-y += scan.o time-event.o rs.o
5iwlmvm-y += power.o bt-coex.o 5iwlmvm-y += power.o bt-coex.o
6iwlmvm-y += led.o 6iwlmvm-y += led.o tt.o
7iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o 7iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
8iwlmvm-$(CONFIG_PM_SLEEP) += d3.o 8iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
9 9
diff --git a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c
index 810bfa5f6de0..f03655f303aa 100644
--- a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c
+++ b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c
@@ -351,6 +351,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
351 enum ieee80211_band band; 351 enum ieee80211_band band;
352 int ave_rssi; 352 int ave_rssi;
353 353
354 lockdep_assert_held(&mvm->mutex);
354 if (vif->type != NL80211_IFTYPE_STATION) 355 if (vif->type != NL80211_IFTYPE_STATION)
355 return; 356 return;
356 357
@@ -365,7 +366,8 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
365 smps_mode = IEEE80211_SMPS_AUTOMATIC; 366 smps_mode = IEEE80211_SMPS_AUTOMATIC;
366 367
367 if (band != IEEE80211_BAND_2GHZ) { 368 if (band != IEEE80211_BAND_2GHZ) {
368 ieee80211_request_smps(vif, smps_mode); 369 iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
370 smps_mode);
369 return; 371 return;
370 } 372 }
371 373
@@ -380,7 +382,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
380 mvmvif->id, data->notif->bt_status, 382 mvmvif->id, data->notif->bt_status,
381 data->notif->bt_traffic_load, smps_mode); 383 data->notif->bt_traffic_load, smps_mode);
382 384
383 ieee80211_request_smps(vif, smps_mode); 385 iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
384 386
385 /* don't reduce the Tx power if in loose scheme */ 387 /* don't reduce the Tx power if in loose scheme */
386 if (is_loose_coex()) 388 if (is_loose_coex())
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
index 733153c3b66c..db6f47406ac2 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
@@ -139,6 +139,9 @@ enum {
139 /* Power */ 139 /* Power */
140 POWER_TABLE_CMD = 0x77, 140 POWER_TABLE_CMD = 0x77,
141 141
142 /* Thermal Throttling*/
143 REPLY_THERMAL_MNG_BACKOFF = 0x7e,
144
142 /* Scanning */ 145 /* Scanning */
143 SCAN_REQUEST_CMD = 0x80, 146 SCAN_REQUEST_CMD = 0x80,
144 SCAN_ABORT_CMD = 0x81, 147 SCAN_ABORT_CMD = 0x81,
@@ -977,4 +980,212 @@ struct iwl_mcast_filter_cmd {
977 u8 addr_list[0]; 980 u8 addr_list[0];
978} __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */ 981} __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */
979 982
983struct mvm_statistics_dbg {
984 __le32 burst_check;
985 __le32 burst_count;
986 __le32 wait_for_silence_timeout_cnt;
987 __le32 reserved[3];
988} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */
989
990struct mvm_statistics_div {
991 __le32 tx_on_a;
992 __le32 tx_on_b;
993 __le32 exec_time;
994 __le32 probe_time;
995 __le32 rssi_ant;
996 __le32 reserved2;
997} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */
998
999struct mvm_statistics_general_common {
1000 __le32 temperature; /* radio temperature */
1001 __le32 temperature_m; /* radio voltage */
1002 struct mvm_statistics_dbg dbg;
1003 __le32 sleep_time;
1004 __le32 slots_out;
1005 __le32 slots_idle;
1006 __le32 ttl_timestamp;
1007 struct mvm_statistics_div div;
1008 __le32 rx_enable_counter;
1009 /*
1010 * num_of_sos_states:
1011 * count the number of times we have to re-tune
1012 * in order to get out of bad PHY status
1013 */
1014 __le32 num_of_sos_states;
1015} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */
1016
1017struct mvm_statistics_rx_non_phy {
1018 __le32 bogus_cts; /* CTS received when not expecting CTS */
1019 __le32 bogus_ack; /* ACK received when not expecting ACK */
1020 __le32 non_bssid_frames; /* number of frames with BSSID that
1021 * doesn't belong to the STA BSSID */
1022 __le32 filtered_frames; /* count frames that were dumped in the
1023 * filtering process */
1024 __le32 non_channel_beacons; /* beacons with our bss id but not on
1025 * our serving channel */
1026 __le32 channel_beacons; /* beacons with our bss id and in our
1027 * serving channel */
1028 __le32 num_missed_bcon; /* number of missed beacons */
1029 __le32 adc_rx_saturation_time; /* count in 0.8us units the time the
1030 * ADC was in saturation */
1031 __le32 ina_detection_search_time;/* total time (in 0.8us) searched
1032 * for INA */
1033 __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */
1034 __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */
1035 __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */
1036 __le32 interference_data_flag; /* flag for interference data
1037 * availability. 1 when data is
1038 * available. */
1039 __le32 channel_load; /* counts RX Enable time in uSec */
1040 __le32 dsp_false_alarms; /* DSP false alarm (both OFDM
1041 * and CCK) counter */
1042 __le32 beacon_rssi_a;
1043 __le32 beacon_rssi_b;
1044 __le32 beacon_rssi_c;
1045 __le32 beacon_energy_a;
1046 __le32 beacon_energy_b;
1047 __le32 beacon_energy_c;
1048 __le32 num_bt_kills;
1049 __le32 mac_id;
1050 __le32 directed_data_mpdu;
1051} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */
1052
1053struct mvm_statistics_rx_phy {
1054 __le32 ina_cnt;
1055 __le32 fina_cnt;
1056 __le32 plcp_err;
1057 __le32 crc32_err;
1058 __le32 overrun_err;
1059 __le32 early_overrun_err;
1060 __le32 crc32_good;
1061 __le32 false_alarm_cnt;
1062 __le32 fina_sync_err_cnt;
1063 __le32 sfd_timeout;
1064 __le32 fina_timeout;
1065 __le32 unresponded_rts;
1066 __le32 rxe_frame_limit_overrun;
1067 __le32 sent_ack_cnt;
1068 __le32 sent_cts_cnt;
1069 __le32 sent_ba_rsp_cnt;
1070 __le32 dsp_self_kill;
1071 __le32 mh_format_err;
1072 __le32 re_acq_main_rssi_sum;
1073 __le32 reserved;
1074} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */
1075
1076struct mvm_statistics_rx_ht_phy {
1077 __le32 plcp_err;
1078 __le32 overrun_err;
1079 __le32 early_overrun_err;
1080 __le32 crc32_good;
1081 __le32 crc32_err;
1082 __le32 mh_format_err;
1083 __le32 agg_crc32_good;
1084 __le32 agg_mpdu_cnt;
1085 __le32 agg_cnt;
1086 __le32 unsupport_mcs;
1087} __packed; /* STATISTICS_HT_RX_PHY_API_S_VER_1 */
1088
1089#define MAX_CHAINS 3
1090
1091struct mvm_statistics_tx_non_phy_agg {
1092 __le32 ba_timeout;
1093 __le32 ba_reschedule_frames;
1094 __le32 scd_query_agg_frame_cnt;
1095 __le32 scd_query_no_agg;
1096 __le32 scd_query_agg;
1097 __le32 scd_query_mismatch;
1098 __le32 frame_not_ready;
1099 __le32 underrun;
1100 __le32 bt_prio_kill;
1101 __le32 rx_ba_rsp_cnt;
1102 __s8 txpower[MAX_CHAINS];
1103 __s8 reserved;
1104 __le32 reserved2;
1105} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */
1106
1107struct mvm_statistics_tx_channel_width {
1108 __le32 ext_cca_narrow_ch20[1];
1109 __le32 ext_cca_narrow_ch40[2];
1110 __le32 ext_cca_narrow_ch80[3];
1111 __le32 ext_cca_narrow_ch160[4];
1112 __le32 last_tx_ch_width_indx;
1113 __le32 rx_detected_per_ch_width[4];
1114 __le32 success_per_ch_width[4];
1115 __le32 fail_per_ch_width[4];
1116}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */
1117
1118struct mvm_statistics_tx {
1119 __le32 preamble_cnt;
1120 __le32 rx_detected_cnt;
1121 __le32 bt_prio_defer_cnt;
1122 __le32 bt_prio_kill_cnt;
1123 __le32 few_bytes_cnt;
1124 __le32 cts_timeout;
1125 __le32 ack_timeout;
1126 __le32 expected_ack_cnt;
1127 __le32 actual_ack_cnt;
1128 __le32 dump_msdu_cnt;
1129 __le32 burst_abort_next_frame_mismatch_cnt;
1130 __le32 burst_abort_missing_next_frame_cnt;
1131 __le32 cts_timeout_collision;
1132 __le32 ack_or_ba_timeout_collision;
1133 struct mvm_statistics_tx_non_phy_agg agg;
1134 struct mvm_statistics_tx_channel_width channel_width;
1135} __packed; /* STATISTICS_TX_API_S_VER_4 */
1136
1137
1138struct mvm_statistics_bt_activity {
1139 __le32 hi_priority_tx_req_cnt;
1140 __le32 hi_priority_tx_denied_cnt;
1141 __le32 lo_priority_tx_req_cnt;
1142 __le32 lo_priority_tx_denied_cnt;
1143 __le32 hi_priority_rx_req_cnt;
1144 __le32 hi_priority_rx_denied_cnt;
1145 __le32 lo_priority_rx_req_cnt;
1146 __le32 lo_priority_rx_denied_cnt;
1147} __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */
1148
1149struct mvm_statistics_general {
1150 struct mvm_statistics_general_common common;
1151 __le32 beacon_filtered;
1152 __le32 missed_beacons;
1153 __s8 beacon_filter_everage_energy;
1154 __s8 beacon_filter_reason;
1155 __s8 beacon_filter_current_energy;
1156 __s8 beacon_filter_reserved;
1157 __le32 beacon_filter_delta_time;
1158 struct mvm_statistics_bt_activity bt_activity;
1159} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */
1160
1161struct mvm_statistics_rx {
1162 struct mvm_statistics_rx_phy ofdm;
1163 struct mvm_statistics_rx_phy cck;
1164 struct mvm_statistics_rx_non_phy general;
1165 struct mvm_statistics_rx_ht_phy ofdm_ht;
1166} __packed; /* STATISTICS_RX_API_S_VER_3 */
1167
1168/*
1169 * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command)
1170 *
1171 * By default, uCode issues this notification after receiving a beacon
1172 * while associated. To disable this behavior, set DISABLE_NOTIF flag in the
1173 * REPLY_STATISTICS_CMD 0x9c, above.
1174 *
1175 * Statistics counters continue to increment beacon after beacon, but are
1176 * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD
1177 * 0x9c with CLEAR_STATS bit set (see above).
1178 *
1179 * uCode also issues this notification during scans. uCode clears statistics
1180 * appropriately so that each notification contains statistics for only the
1181 * one channel that has just been scanned.
1182 */
1183
1184struct iwl_notif_statistics { /* STATISTICS_NTFY_API_S_VER_8 */
1185 __le32 flag;
1186 struct mvm_statistics_rx rx;
1187 struct mvm_statistics_tx tx;
1188 struct mvm_statistics_general general;
1189} __packed;
1190
980#endif /* __fw_api_h__ */ 1191#endif /* __fw_api_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
index 20ee2812cc9b..cd7c0032cc58 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
@@ -330,7 +330,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
330 * abort after reading the nvm in case RF Kill is on, we will complete 330 * abort after reading the nvm in case RF Kill is on, we will complete
331 * the init seq later when RF kill will switch to off 331 * the init seq later when RF kill will switch to off
332 */ 332 */
333 if (test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status)) { 333 if (iwl_mvm_is_radio_killed(mvm)) {
334 IWL_DEBUG_RF_KILL(mvm, 334 IWL_DEBUG_RF_KILL(mvm,
335 "jump over all phy activities due to RF kill\n"); 335 "jump over all phy activities due to RF kill\n");
336 iwl_remove_notification(&mvm->notif_wait, &calib_wait); 336 iwl_remove_notification(&mvm->notif_wait, &calib_wait);
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
index b2cc3d98e0f7..dc5f4ef50198 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
@@ -227,7 +227,7 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
227 .found_vif = false, 227 .found_vif = false,
228 }; 228 };
229 u32 ac; 229 u32 ac;
230 int ret; 230 int ret, i;
231 231
232 /* 232 /*
233 * Allocate a MAC ID and a TSF for this MAC, along with the queues 233 * Allocate a MAC ID and a TSF for this MAC, along with the queues
@@ -335,6 +335,9 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
335 mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT; 335 mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT;
336 mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; 336 mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
337 337
338 for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++)
339 mvmvif->smps_requests[i] = IEEE80211_SMPS_AUTOMATIC;
340
338 return 0; 341 return 0;
339 342
340exit_fail: 343exit_fail:
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index c9924c12e0fe..c26f6b504460 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -265,8 +265,8 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
265{ 265{
266 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); 266 struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
267 267
268 if (test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status)) { 268 if (iwl_mvm_is_radio_killed(mvm)) {
269 IWL_DEBUG_DROP(mvm, "Dropping - RF KILL\n"); 269 IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n");
270 goto drop; 270 goto drop;
271 } 271 }
272 272
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index 4e87a321e107..a288552491b3 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -150,6 +150,12 @@ enum iwl_power_scheme {
150 150
151#define IWL_CONN_MAX_LISTEN_INTERVAL 70 151#define IWL_CONN_MAX_LISTEN_INTERVAL 70
152 152
153enum iwl_mvm_smps_type_request {
154 IWL_MVM_SMPS_REQ_BT_COEX,
155 IWL_MVM_SMPS_REQ_TT,
156 NUM_IWL_MVM_SMPS_REQ,
157};
158
153/** 159/**
154 * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context 160 * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context
155 * @id: between 0 and 3 161 * @id: between 0 and 3
@@ -164,6 +170,8 @@ enum iwl_power_scheme {
164 * @bcast_sta: station used for broadcast packets. Used by the following 170 * @bcast_sta: station used for broadcast packets. Used by the following
165 * vifs: P2P_DEVICE, GO and AP. 171 * vifs: P2P_DEVICE, GO and AP.
166 * @beacon_skb: the skb used to hold the AP/GO beacon template 172 * @beacon_skb: the skb used to hold the AP/GO beacon template
173 * @smps_requests: the requests of of differents parts of the driver, regard
174 the desired smps mode.
167 */ 175 */
168struct iwl_mvm_vif { 176struct iwl_mvm_vif {
169 u16 id; 177 u16 id;
@@ -218,6 +226,8 @@ struct iwl_mvm_vif {
218 struct dentry *dbgfs_slink; 226 struct dentry *dbgfs_slink;
219 void *dbgfs_data; 227 void *dbgfs_data;
220#endif 228#endif
229
230 enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ];
221}; 231};
222 232
223static inline struct iwl_mvm_vif * 233static inline struct iwl_mvm_vif *
@@ -226,12 +236,6 @@ iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif)
226 return (void *)vif->drv_priv; 236 return (void *)vif->drv_priv;
227} 237}
228 238
229enum iwl_mvm_status {
230 IWL_MVM_STATUS_HW_RFKILL,
231 IWL_MVM_STATUS_ROC_RUNNING,
232 IWL_MVM_STATUS_IN_HW_RESTART,
233};
234
235enum iwl_scan_status { 239enum iwl_scan_status {
236 IWL_MVM_SCAN_NONE, 240 IWL_MVM_SCAN_NONE,
237 IWL_MVM_SCAN_OS, 241 IWL_MVM_SCAN_OS,
@@ -249,6 +253,63 @@ struct iwl_nvm_section {
249 const u8 *data; 253 const u8 *data;
250}; 254};
251 255
256/*
257 * Tx-backoff threshold
258 * @temperature: The threshold in Celsius
259 * @backoff: The tx-backoff in uSec
260 */
261struct iwl_tt_tx_backoff {
262 s32 temperature;
263 u32 backoff;
264};
265
266#define TT_TX_BACKOFF_SIZE 6
267
268/**
269 * struct iwl_tt_params - thermal throttling parameters
270 * @ct_kill_entry: CT Kill entry threshold
271 * @ct_kill_exit: CT Kill exit threshold
272 * @ct_kill_duration: The time intervals (in uSec) in which the driver needs
273 * to checks whether to exit CT Kill.
274 * @dynamic_smps_entry: Dynamic SMPS entry threshold
275 * @dynamic_smps_exit: Dynamic SMPS exit threshold
276 * @tx_protection_entry: TX protection entry threshold
277 * @tx_protection_exit: TX protection exit threshold
278 * @tx_backoff: Array of thresholds for tx-backoff , in ascending order.
279 * @support_ct_kill: Support CT Kill?
280 * @support_dynamic_smps: Support dynamic SMPS?
281 * @support_tx_protection: Support tx protection?
282 * @support_tx_backoff: Support tx-backoff?
283 */
284struct iwl_tt_params {
285 s32 ct_kill_entry;
286 s32 ct_kill_exit;
287 u32 ct_kill_duration;
288 s32 dynamic_smps_entry;
289 s32 dynamic_smps_exit;
290 s32 tx_protection_entry;
291 s32 tx_protection_exit;
292 struct iwl_tt_tx_backoff tx_backoff[TT_TX_BACKOFF_SIZE];
293 bool support_ct_kill;
294 bool support_dynamic_smps;
295 bool support_tx_protection;
296 bool support_tx_backoff;
297};
298
299/**
300 * struct iwl_mvm_tt_mgnt - Thermal Throttling Management structure
301 * @ct_kill_exit: worker to exit thermal kill
302 * @dynamic_smps: Is thermal throttling enabled dynamic_smps?
303 * @tx_backoff: The current thremal throttling tx backoff in uSec.
304 * @params: Parameters to configure the thermal throttling algorithm.
305 */
306struct iwl_mvm_tt_mgmt {
307 struct delayed_work ct_kill_exit;
308 bool dynamic_smps;
309 u32 tx_backoff;
310 const struct iwl_tt_params *params;
311};
312
252struct iwl_mvm { 313struct iwl_mvm {
253 /* for logger access */ 314 /* for logger access */
254 struct device *dev; 315 struct device *dev;
@@ -356,6 +417,10 @@ struct iwl_mvm {
356 /* BT-Coex */ 417 /* BT-Coex */
357 u8 bt_kill_msk; 418 u8 bt_kill_msk;
358 struct iwl_bt_coex_profile_notif last_bt_notif; 419 struct iwl_bt_coex_profile_notif last_bt_notif;
420
421 /* Thermal Throttling and CTkill */
422 struct iwl_mvm_tt_mgmt thermal_throttle;
423 s32 temperature; /* Celsius */
359}; 424};
360 425
361/* Extract MVM priv from op_mode and _hw */ 426/* Extract MVM priv from op_mode and _hw */
@@ -365,6 +430,19 @@ struct iwl_mvm {
365#define IWL_MAC80211_GET_MVM(_hw) \ 430#define IWL_MAC80211_GET_MVM(_hw) \
366 IWL_OP_MODE_GET_MVM((struct iwl_op_mode *)((_hw)->priv)) 431 IWL_OP_MODE_GET_MVM((struct iwl_op_mode *)((_hw)->priv))
367 432
433enum iwl_mvm_status {
434 IWL_MVM_STATUS_HW_RFKILL,
435 IWL_MVM_STATUS_HW_CTKILL,
436 IWL_MVM_STATUS_ROC_RUNNING,
437 IWL_MVM_STATUS_IN_HW_RESTART,
438};
439
440static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
441{
442 return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status) ||
443 test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
444}
445
368extern const u8 iwl_mvm_ac_to_tx_fifo[]; 446extern const u8 iwl_mvm_ac_to_tx_fifo[];
369 447
370struct iwl_rate_info { 448struct iwl_rate_info {
@@ -555,4 +633,15 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
555int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, 633int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
556 struct ieee80211_vif *vif); 634 struct ieee80211_vif *vif);
557 635
636/* SMPS */
637void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
638 enum iwl_mvm_smps_type_request req_type,
639 enum ieee80211_smps_mode smps_request);
640
641/* Thermal management and CT-kill */
642void iwl_mvm_tt_handler(struct iwl_mvm *mvm);
643void iwl_mvm_tt_initialize(struct iwl_mvm *mvm);
644void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
645void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
646
558#endif /* __IWL_MVM_H__ */ 647#endif /* __IWL_MVM_H__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index 7998baca5a8b..8431637e38f2 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -222,6 +222,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
222 222
223 RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true), 223 RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true),
224 RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false), 224 RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false),
225 RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true),
225 226
226 RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false), 227 RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false),
227 RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false), 228 RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false),
@@ -294,6 +295,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
294 CMD(BT_CONFIG), 295 CMD(BT_CONFIG),
295 CMD(MCAST_FILTER_CMD), 296 CMD(MCAST_FILTER_CMD),
296 CMD(REPLY_BEACON_FILTERING_CMD), 297 CMD(REPLY_BEACON_FILTERING_CMD),
298 CMD(REPLY_THERMAL_MNG_BACKOFF),
297}; 299};
298#undef CMD 300#undef CMD
299 301
@@ -394,6 +396,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
394 if (err) 396 if (err)
395 goto out_free; 397 goto out_free;
396 398
399 iwl_mvm_tt_initialize(mvm);
400
397 mutex_lock(&mvm->mutex); 401 mutex_lock(&mvm->mutex);
398 err = iwl_run_init_mvm_ucode(mvm, true); 402 err = iwl_run_init_mvm_ucode(mvm, true);
399 mutex_unlock(&mvm->mutex); 403 mutex_unlock(&mvm->mutex);
@@ -441,6 +445,8 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
441 445
442 iwl_mvm_leds_exit(mvm); 446 iwl_mvm_leds_exit(mvm);
443 447
448 iwl_mvm_tt_exit(mvm);
449
444 ieee80211_unregister_hw(mvm->hw); 450 ieee80211_unregister_hw(mvm->hw);
445 451
446 kfree(mvm->scan_cmd); 452 kfree(mvm->scan_cmd);
@@ -595,6 +601,16 @@ static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue)
595 ieee80211_wake_queue(mvm->hw, mq); 601 ieee80211_wake_queue(mvm->hw, mq);
596} 602}
597 603
604void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state)
605{
606 if (state)
607 set_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
608 else
609 clear_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
610
611 wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
612}
613
598static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) 614static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
599{ 615{
600 struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); 616 struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
@@ -604,7 +620,7 @@ static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
604 else 620 else
605 clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); 621 clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
606 622
607 wiphy_rfkill_set_hw_state(mvm->hw->wiphy, state); 623 wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
608} 624}
609 625
610static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) 626static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c
index 55334d542e26..6a050c69e7d0 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.c
@@ -3080,3 +3080,29 @@ void iwl_mvm_rate_control_unregister(void)
3080{ 3080{
3081 ieee80211_rate_control_unregister(&rs_mvm_ops); 3081 ieee80211_rate_control_unregister(&rs_mvm_ops);
3082} 3082}
3083
3084/**
3085 * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable
3086 * Tx protection, according to this rquest and previous requests,
3087 * and send the LQ command.
3088 * @lq: The LQ command
3089 * @mvmsta: The station
3090 * @enable: Enable Tx protection?
3091 */
3092int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
3093 struct iwl_mvm_sta *mvmsta, bool enable)
3094{
3095 lockdep_assert_held(&mvm->mutex);
3096
3097 if (enable) {
3098 if (mvmsta->tx_protection == 0)
3099 lq->flags |= LQ_FLAG_SET_STA_TLC_RTS_MSK;
3100 mvmsta->tx_protection++;
3101 } else {
3102 mvmsta->tx_protection--;
3103 if (mvmsta->tx_protection == 0)
3104 lq->flags &= ~LQ_FLAG_SET_STA_TLC_RTS_MSK;
3105 }
3106
3107 return iwl_mvm_send_lq_cmd(mvm, lq, CMD_ASYNC, false);
3108}
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h
index 219c6857cc0f..f66155a57238 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.h
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.h
@@ -390,4 +390,9 @@ extern int iwl_mvm_rate_control_register(void);
390 */ 390 */
391extern void iwl_mvm_rate_control_unregister(void); 391extern void iwl_mvm_rate_control_unregister(void);
392 392
393struct iwl_mvm_sta;
394
395int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
396 struct iwl_mvm_sta *mvmsta, bool enable);
397
393#endif /* __rs__ */ 398#endif /* __rs__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c
index 4dfc21a3e83e..e4930d5027d2 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rx.c
@@ -363,3 +363,25 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
363 rxb, &rx_status); 363 rxb, &rx_status);
364 return 0; 364 return 0;
365} 365}
366
367/*
368 * iwl_mvm_rx_statistics - STATISTICS_NOTIFICATION handler
369 *
370 * TODO: This handler is implemented partially.
371 * It only gets the NIC's temperature.
372 */
373int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
374 struct iwl_rx_cmd_buffer *rxb,
375 struct iwl_device_cmd *cmd)
376{
377 struct iwl_rx_packet *pkt = rxb_addr(rxb);
378 struct iwl_notif_statistics *stats = (void *)&pkt->data;
379 struct mvm_statistics_general_common *common = &stats->general.common;
380
381 if (mvm->temperature != le32_to_cpu(common->temperature)) {
382 mvm->temperature = le32_to_cpu(common->temperature);
383 iwl_mvm_tt_handler(mvm);
384 }
385
386 return 0;
387}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
index 5c664ed54400..2278858d5658 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.c
@@ -64,6 +64,7 @@
64 64
65#include "mvm.h" 65#include "mvm.h"
66#include "sta.h" 66#include "sta.h"
67#include "rs.h"
67 68
68static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm) 69static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm)
69{ 70{
@@ -217,6 +218,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
217 mvmvif->color); 218 mvmvif->color);
218 mvm_sta->vif = vif; 219 mvm_sta->vif = vif;
219 mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF; 220 mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
221 mvm_sta->tx_protection = 0;
222 mvm_sta->tt_tx_protection = false;
220 223
221 /* HW restart, don't assume the memory has been zeroed */ 224 /* HW restart, don't assume the memory has been zeroed */
222 atomic_set(&mvm->pending_frames[sta_id], 0); 225 atomic_set(&mvm->pending_frames[sta_id], 0);
@@ -798,21 +801,23 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
798 min(mvmsta->max_agg_bufsize, buf_size); 801 min(mvmsta->max_agg_bufsize, buf_size);
799 mvmsta->lq_sta.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize; 802 mvmsta->lq_sta.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize;
800 803
804 IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",
805 sta->addr, tid);
806
801 if (mvm->cfg->ht_params->use_rts_for_aggregation) { 807 if (mvm->cfg->ht_params->use_rts_for_aggregation) {
802 /* 808 /*
803 * switch to RTS/CTS if it is the prefer protection 809 * switch to RTS/CTS if it is the prefer protection
804 * method for HT traffic 810 * method for HT traffic
811 * this function also sends the LQ command
805 */ 812 */
806 mvmsta->lq_sta.lq.flags |= LQ_FLAG_SET_STA_TLC_RTS_MSK; 813 return iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq,
814 mvmsta, true);
807 /* 815 /*
808 * TODO: remove the TLC_RTS flag when we tear down the last 816 * TODO: remove the TLC_RTS flag when we tear down the last
809 * AGG session (agg_tids_count in DVM) 817 * AGG session (agg_tids_count in DVM)
810 */ 818 */
811 } 819 }
812 820
813 IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",
814 sta->addr, tid);
815
816 return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, CMD_ASYNC, false); 821 return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, CMD_ASYNC, false);
817} 822}
818 823
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h
index a4ddce77aaae..3efa0a0cc987 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.h
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.h
@@ -275,6 +275,8 @@ struct iwl_mvm_tid_data {
275 * @lock: lock to protect the whole struct. Since %tid_data is access from Tx 275 * @lock: lock to protect the whole struct. Since %tid_data is access from Tx
276 * and from Tx response flow, it needs a spinlock. 276 * and from Tx response flow, it needs a spinlock.
277 * @tid_data: per tid data. Look at %iwl_mvm_tid_data. 277 * @tid_data: per tid data. Look at %iwl_mvm_tid_data.
278 * @tx_protection: reference counter for controlling the Tx protection.
279 * @tt_tx_protection: is thermal throttling enable Tx protection?
278 * 280 *
279 * When mac80211 creates a station it reserves some space (hw->sta_data_size) 281 * When mac80211 creates a station it reserves some space (hw->sta_data_size)
280 * in the structure for use by driver. This structure is placed in that 282 * in the structure for use by driver. This structure is placed in that
@@ -296,6 +298,10 @@ struct iwl_mvm_sta {
296#ifdef CONFIG_PM_SLEEP 298#ifdef CONFIG_PM_SLEEP
297 u16 last_seq_ctl; 299 u16 last_seq_ctl;
298#endif 300#endif
301
302 /* Temporary, until the new TLC will control the Tx protection */
303 s8 tx_protection;
304 bool tt_tx_protection;
299}; 305};
300 306
301/** 307/**
diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c
new file mode 100644
index 000000000000..4665fc033c17
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/tt.c
@@ -0,0 +1,509 @@
1/******************************************************************************
2 *
3 * This file is provided under a dual BSD/GPLv2 license. When using or
4 * redistributing this file, you may do so under either license.
5 *
6 * GPL LICENSE SUMMARY
7 *
8 * Copyright(c) 2013 Intel Corporation. All rights reserved.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of version 2 of the GNU General Public License as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
22 * USA
23 *
24 * The full GNU General Public License is included in this distribution
25 * in the file called COPYING.
26 *
27 * Contact Information:
28 * Intel Linux Wireless <ilw@linux.intel.com>
29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30 *
31 * BSD LICENSE
32 *
33 * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 *
40 * * Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * * Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in
44 * the documentation and/or other materials provided with the
45 * distribution.
46 * * Neither the name Intel Corporation nor the names of its
47 * contributors may be used to endorse or promote products derived
48 * from this software without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
54 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
56 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
60 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61 *
62 *****************************************************************************/
63
64#include "mvm.h"
65#include "iwl-config.h"
66#include "iwl-io.h"
67#include "iwl-csr.h"
68#include "iwl-prph.h"
69
70#define OTP_DTS_DIODE_DEVIATION 96 /*in words*/
71/* VBG - Voltage Band Gap error data (temperature offset) */
72#define OTP_WP_DTS_VBG (OTP_DTS_DIODE_DEVIATION + 2)
73#define MEAS_VBG_MIN_VAL 2300
74#define MEAS_VBG_MAX_VAL 3000
75#define MEAS_VBG_DEFAULT_VAL 2700
76#define DTS_DIODE_VALID(flags) (flags & DTS_DIODE_REG_FLAGS_PASS_ONCE)
77#define MIN_TEMPERATURE 0
78#define MAX_TEMPERATURE 125
79#define TEMPERATURE_ERROR (MAX_TEMPERATURE + 1)
80#define PTAT_DIGITAL_VALUE_MIN_VALUE 0
81#define PTAT_DIGITAL_VALUE_MAX_VALUE 0xFF
82#define DTS_VREFS_NUM 5
83static inline u32 DTS_DIODE_GET_VREFS_ID(u32 flags)
84{
85 return (flags & DTS_DIODE_REG_FLAGS_VREFS_ID) >>
86 DTS_DIODE_REG_FLAGS_VREFS_ID_POS;
87}
88
89#define CALC_VREFS_MIN_DIFF 43
90#define CALC_VREFS_MAX_DIFF 51
91#define CALC_LUT_SIZE (1 + CALC_VREFS_MAX_DIFF - CALC_VREFS_MIN_DIFF)
92#define CALC_LUT_INDEX_OFFSET CALC_VREFS_MIN_DIFF
93#define CALC_TEMPERATURE_RESULT_SHIFT_OFFSET 23
94
95/*
96 * @digital_value: The diode's digital-value sampled (temperature/voltage)
97 * @vref_low: The lower voltage-reference (the vref just below the diode's
98 * sampled digital-value)
99 * @vref_high: The higher voltage-reference (the vref just above the diode's
100 * sampled digital-value)
101 * @flags: bits[1:0]: The ID of the Vrefs pair (lowVref,highVref)
102 * bits[6:2]: Reserved.
103 * bits[7:7]: Indicates completion of at least 1 successful sample
104 * since last DTS reset.
105 */
106struct iwl_mvm_dts_diode_bits {
107 u8 digital_value;
108 u8 vref_low;
109 u8 vref_high;
110 u8 flags;
111} __packed;
112
113union dts_diode_results {
114 u32 reg_value;
115 struct iwl_mvm_dts_diode_bits bits;
116} __packed;
117
118static s16 iwl_mvm_dts_get_volt_band_gap(struct iwl_mvm *mvm)
119{
120 struct iwl_nvm_section calib_sec;
121 const __le16 *calib;
122 u16 vbg;
123
124 /* TODO: move parsing to NVM code */
125 calib_sec = mvm->nvm_sections[NVM_SECTION_TYPE_CALIBRATION];
126 calib = (__le16 *)calib_sec.data;
127
128 vbg = le16_to_cpu(calib[OTP_WP_DTS_VBG]);
129
130 if (vbg < MEAS_VBG_MIN_VAL || vbg > MEAS_VBG_MAX_VAL)
131 vbg = MEAS_VBG_DEFAULT_VAL;
132
133 return vbg;
134}
135
136static u16 iwl_mvm_dts_get_ptat_deviation_offset(struct iwl_mvm *mvm)
137{
138 const u8 *calib;
139 u8 ptat, pa1, pa2, median;
140
141 /* TODO: move parsing to NVM code */
142 calib = mvm->nvm_sections[NVM_SECTION_TYPE_CALIBRATION].data;
143 ptat = calib[OTP_DTS_DIODE_DEVIATION];
144 pa1 = calib[OTP_DTS_DIODE_DEVIATION + 1];
145 pa2 = calib[OTP_DTS_DIODE_DEVIATION + 2];
146
147 /* get the median: */
148 if (ptat > pa1) {
149 if (ptat > pa2)
150 median = (pa1 > pa2) ? pa1 : pa2;
151 else
152 median = ptat;
153 } else {
154 if (pa1 > pa2)
155 median = (ptat > pa2) ? ptat : pa2;
156 else
157 median = pa1;
158 }
159
160 return ptat - median;
161}
162
163static u8 iwl_mvm_dts_calibrate_ptat_deviation(struct iwl_mvm *mvm, u8 value)
164{
165 /* Calibrate the PTAT digital value, based on PTAT deviation data: */
166 s16 new_val = value - iwl_mvm_dts_get_ptat_deviation_offset(mvm);
167
168 if (new_val > PTAT_DIGITAL_VALUE_MAX_VALUE)
169 new_val = PTAT_DIGITAL_VALUE_MAX_VALUE;
170 else if (new_val < PTAT_DIGITAL_VALUE_MIN_VALUE)
171 new_val = PTAT_DIGITAL_VALUE_MIN_VALUE;
172
173 return new_val;
174}
175
176static bool dts_get_adjacent_vrefs(struct iwl_mvm *mvm,
177 union dts_diode_results *avg_ptat)
178{
179 u8 vrefs_results[DTS_VREFS_NUM];
180 u8 low_vref_index = 0, flags;
181 u32 reg;
182
183 reg = iwl_read_prph(mvm->trans, DTSC_VREF_AVG);
184 memcpy(vrefs_results, &reg, sizeof(reg));
185 reg = iwl_read_prph(mvm->trans, DTSC_VREF5_AVG);
186 vrefs_results[4] = reg & 0xff;
187
188 if (avg_ptat->bits.digital_value < vrefs_results[0] ||
189 avg_ptat->bits.digital_value > vrefs_results[4])
190 return false;
191
192 if (avg_ptat->bits.digital_value > vrefs_results[3])
193 low_vref_index = 3;
194 else if (avg_ptat->bits.digital_value > vrefs_results[2])
195 low_vref_index = 2;
196 else if (avg_ptat->bits.digital_value > vrefs_results[1])
197 low_vref_index = 1;
198
199 avg_ptat->bits.vref_low = vrefs_results[low_vref_index];
200 avg_ptat->bits.vref_high = vrefs_results[low_vref_index + 1];
201 flags = avg_ptat->bits.flags;
202 avg_ptat->bits.flags =
203 (flags & ~DTS_DIODE_REG_FLAGS_VREFS_ID) |
204 (low_vref_index & DTS_DIODE_REG_FLAGS_VREFS_ID);
205 return true;
206}
207
208/*
209 * return true it the results are valid, and false otherwise.
210 */
211static bool dts_read_ptat_avg_results(struct iwl_mvm *mvm,
212 union dts_diode_results *avg_ptat)
213{
214 u32 reg;
215 u8 tmp;
216
217 /* fill the diode value and pass_once with avg-reg results */
218 reg = iwl_read_prph(mvm->trans, DTSC_PTAT_AVG);
219 reg &= DTS_DIODE_REG_DIG_VAL | DTS_DIODE_REG_PASS_ONCE;
220 avg_ptat->reg_value = reg;
221
222 /* calibrate the PTAT digital value */
223 tmp = avg_ptat->bits.digital_value;
224 tmp = iwl_mvm_dts_calibrate_ptat_deviation(mvm, tmp);
225 avg_ptat->bits.digital_value = tmp;
226
227 /*
228 * fill vrefs fields, based on the avgVrefs results
229 * and the diode value
230 */
231 return dts_get_adjacent_vrefs(mvm, avg_ptat) &&
232 DTS_DIODE_VALID(avg_ptat->bits.flags);
233}
234
235static s32 calculate_nic_temperature(union dts_diode_results avg_ptat,
236 u16 volt_band_gap)
237{
238 u32 tmp_result;
239 u8 vrefs_diff;
240 /*
241 * For temperature calculation (at the end, shift right by 23)
242 * LUT[(D2-D1)] = ROUND{ 2^23 / ((D2-D1)*9*10) }
243 * (D2-D1) == 43 44 45 46 47 48 49 50 51
244 */
245 static const u16 calc_lut[CALC_LUT_SIZE] = {
246 2168, 2118, 2071, 2026, 1983, 1942, 1902, 1864, 1828,
247 };
248
249 /*
250 * The diff between the high and low voltage-references is assumed
251 * to be strictly be in range of [60,68]
252 */
253 vrefs_diff = avg_ptat.bits.vref_high - avg_ptat.bits.vref_low;
254
255 if (vrefs_diff < CALC_VREFS_MIN_DIFF ||
256 vrefs_diff > CALC_VREFS_MAX_DIFF)
257 return TEMPERATURE_ERROR;
258
259 /* calculate the result: */
260 tmp_result =
261 vrefs_diff * (DTS_DIODE_GET_VREFS_ID(avg_ptat.bits.flags) + 9);
262 tmp_result += avg_ptat.bits.digital_value;
263 tmp_result -= avg_ptat.bits.vref_high;
264
265 /* multiply by the LUT value (based on the diff) */
266 tmp_result *= calc_lut[vrefs_diff - CALC_LUT_INDEX_OFFSET];
267
268 /*
269 * Get the BandGap (the voltage refereces source) error data
270 * (temperature offset)
271 */
272 tmp_result *= volt_band_gap;
273
274 /*
275 * here, tmp_result value can be up to 32-bits. We want to right-shift
276 * it *without* sign-extend.
277 */
278 tmp_result = tmp_result >> CALC_TEMPERATURE_RESULT_SHIFT_OFFSET;
279
280 /*
281 * at this point, tmp_result should be in the range:
282 * 200 <= tmp_result <= 365
283 */
284 return (s16)tmp_result - 240;
285}
286
287static s32 check_nic_temperature(struct iwl_mvm *mvm)
288{
289 u16 volt_band_gap;
290 union dts_diode_results avg_ptat;
291
292 volt_band_gap = iwl_mvm_dts_get_volt_band_gap(mvm);
293
294 /* disable DTS */
295 iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 0);
296
297 /* SV initialization */
298 iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 1);
299 iwl_write_prph(mvm->trans, DTSC_CFG_MODE,
300 DTSC_CFG_MODE_PERIODIC);
301
302 /* wait for results */
303 msleep(100);
304 if (!dts_read_ptat_avg_results(mvm, &avg_ptat))
305 return TEMPERATURE_ERROR;
306
307 /* disable DTS */
308 iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 0);
309
310 return calculate_nic_temperature(avg_ptat, volt_band_gap);
311}
312
313static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm)
314{
315 u32 duration = mvm->thermal_throttle.params->ct_kill_duration;
316
317 IWL_ERR(mvm, "Enter CT Kill\n");
318 iwl_mvm_set_hw_ctkill_state(mvm, true);
319 schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
320 round_jiffies_relative(duration * HZ));
321}
322
323static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm)
324{
325 IWL_ERR(mvm, "Exit CT Kill\n");
326 iwl_mvm_set_hw_ctkill_state(mvm, false);
327}
328
329static void check_exit_ctkill(struct work_struct *work)
330{
331 struct iwl_mvm_tt_mgmt *tt;
332 struct iwl_mvm *mvm;
333 u32 duration;
334 s32 temp;
335
336 tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work);
337 mvm = container_of(tt, struct iwl_mvm, thermal_throttle);
338
339 duration = tt->params->ct_kill_duration;
340
341 iwl_trans_start_hw(mvm->trans);
342 temp = check_nic_temperature(mvm);
343 iwl_trans_stop_hw(mvm->trans, false);
344
345 if (temp < MIN_TEMPERATURE || temp > MAX_TEMPERATURE) {
346 IWL_DEBUG_TEMP(mvm, "Failed to measure NIC temperature\n");
347 goto reschedule;
348 }
349 IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp);
350
351 if (temp <= tt->params->ct_kill_exit) {
352 iwl_mvm_exit_ctkill(mvm);
353 return;
354 }
355
356reschedule:
357 schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
358 round_jiffies(duration * HZ));
359}
360
361static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac,
362 struct ieee80211_vif *vif)
363{
364 struct iwl_mvm *mvm = _data;
365 enum ieee80211_smps_mode smps_mode;
366
367 lockdep_assert_held(&mvm->mutex);
368
369 if (mvm->thermal_throttle.dynamic_smps)
370 smps_mode = IEEE80211_SMPS_DYNAMIC;
371 else
372 smps_mode = IEEE80211_SMPS_AUTOMATIC;
373
374 iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode);
375}
376
377static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
378{
379 struct ieee80211_sta *sta;
380 struct iwl_mvm_sta *mvmsta;
381 int i, err;
382
383 for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
384 sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
385 lockdep_is_held(&mvm->mutex));
386 if (IS_ERR_OR_NULL(sta))
387 continue;
388 mvmsta = (void *)sta->drv_priv;
389 if (enable == mvmsta->tt_tx_protection)
390 continue;
391 err = iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq,
392 mvmsta, enable);
393 if (err) {
394 IWL_ERR(mvm, "Failed to %s Tx protection\n",
395 enable ? "enable" : "disable");
396 } else {
397 IWL_DEBUG_TEMP(mvm, "%s Tx protection\n",
398 enable ? "Enable" : "Disable");
399 mvmsta->tt_tx_protection = enable;
400 }
401 }
402}
403
404static void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
405{
406 struct iwl_host_cmd cmd = {
407 .id = REPLY_THERMAL_MNG_BACKOFF,
408 .len = { sizeof(u32), },
409 .data = { &backoff, },
410 .flags = CMD_SYNC,
411 };
412
413 if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {
414 IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",
415 backoff);
416 mvm->thermal_throttle.tx_backoff = backoff;
417 } else {
418 IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n");
419 }
420}
421
422void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
423{
424 const struct iwl_tt_params *params = mvm->thermal_throttle.params;
425 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
426 s32 temperature = mvm->temperature;
427 int i;
428 u32 tx_backoff;
429
430 IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature);
431
432 if (params->support_ct_kill && temperature >= params->ct_kill_entry) {
433 iwl_mvm_enter_ctkill(mvm);
434 return;
435 }
436
437 if (params->support_dynamic_smps) {
438 if (!tt->dynamic_smps &&
439 temperature >= params->dynamic_smps_entry) {
440 IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n");
441 tt->dynamic_smps = true;
442 ieee80211_iterate_active_interfaces_atomic(
443 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
444 iwl_mvm_tt_smps_iterator, mvm);
445 } else if (tt->dynamic_smps &&
446 temperature <= params->dynamic_smps_exit) {
447 IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n");
448 tt->dynamic_smps = false;
449 ieee80211_iterate_active_interfaces_atomic(
450 mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
451 iwl_mvm_tt_smps_iterator, mvm);
452 }
453 }
454
455 if (params->support_tx_protection) {
456 if (temperature >= params->tx_protection_entry)
457 iwl_mvm_tt_tx_protection(mvm, true);
458 else if (temperature <= params->tx_protection_exit)
459 iwl_mvm_tt_tx_protection(mvm, false);
460 }
461
462 if (params->support_tx_backoff) {
463 tx_backoff = 0;
464 for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) {
465 if (temperature < params->tx_backoff[i].temperature)
466 break;
467 tx_backoff = params->tx_backoff[i].backoff;
468 }
469 if (tt->tx_backoff != tx_backoff)
470 iwl_mvm_tt_tx_backoff(mvm, tx_backoff);
471 }
472}
473
474static const struct iwl_tt_params iwl7000_tt_params = {
475 .ct_kill_entry = 118,
476 .ct_kill_exit = 96,
477 .ct_kill_duration = 5,
478 .dynamic_smps_entry = 114,
479 .dynamic_smps_exit = 110,
480 .tx_protection_entry = 114,
481 .tx_protection_exit = 108,
482 .tx_backoff = {
483 {.temperature = 112, .backoff = 200},
484 {.temperature = 113, .backoff = 600},
485 {.temperature = 114, .backoff = 1200},
486 {.temperature = 115, .backoff = 2000},
487 {.temperature = 116, .backoff = 4000},
488 {.temperature = 117, .backoff = 10000},
489 },
490 .support_ct_kill = true,
491 .support_dynamic_smps = true,
492 .support_tx_protection = true,
493 .support_tx_backoff = true,
494};
495
496void iwl_mvm_tt_initialize(struct iwl_mvm *mvm)
497{
498 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
499
500 IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
501 tt->params = &iwl7000_tt_params;
502 INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
503}
504
505void iwl_mvm_tt_exit(struct iwl_mvm *mvm)
506{
507 cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
508 IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
509}
diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c
index 687b34e387ac..c9b44ab4af07 100644
--- a/drivers/net/wireless/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/iwlwifi/mvm/utils.c
@@ -471,3 +471,34 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
471 471
472 return iwl_mvm_send_cmd(mvm, &cmd); 472 return iwl_mvm_send_cmd(mvm, &cmd);
473} 473}
474
475/**
476 * iwl_mvm_update_smps - Get a requst to change the SMPS mode
477 * @req_type: The part of the driver who call for a change.
478 * @smps_requests: The request to change the SMPS mode.
479 *
480 * Get a requst to change the SMPS mode,
481 * and change it according to all other requests in the driver.
482 */
483void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
484 enum iwl_mvm_smps_type_request req_type,
485 enum ieee80211_smps_mode smps_request)
486{
487 struct iwl_mvm_vif *mvmvif;
488 enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC;
489 int i;
490
491 lockdep_assert_held(&mvm->mutex);
492 mvmvif = iwl_mvm_vif_from_mac80211(vif);
493 mvmvif->smps_requests[req_type] = smps_request;
494 for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) {
495 if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC) {
496 smps_mode = IEEE80211_SMPS_STATIC;
497 break;
498 }
499 if (mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC)
500 smps_mode = IEEE80211_SMPS_DYNAMIC;
501 }
502
503 ieee80211_request_smps(vif, smps_mode);
504}