18e99ea8dSJohannes Berg // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
28e99ea8dSJohannes Berg /*
3*1a3e7039SGregory Greenman * Copyright (C) 2012-2014, 2019-2022 Intel Corporation
48e99ea8dSJohannes Berg * Copyright (C) 2013-2014 Intel Mobile Communications GmbH
58e99ea8dSJohannes Berg * Copyright (C) 2015-2016 Intel Deutschland GmbH
68e99ea8dSJohannes Berg */
7c221daf2SChaya Rachel Ivgi #include <linux/sort.h>
8c221daf2SChaya Rachel Ivgi
9e705c121SKalle Valo #include "mvm.h"
10e705c121SKalle Valo
11e705c121SKalle Valo #define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ
12e705c121SKalle Valo
iwl_mvm_enter_ctkill(struct iwl_mvm * mvm)1361d8c626SChaya Rachel Ivgi void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm)
14e705c121SKalle Valo {
15e705c121SKalle Valo struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
16e705c121SKalle Valo u32 duration = tt->params.ct_kill_duration;
17e705c121SKalle Valo
18e705c121SKalle Valo if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
19e705c121SKalle Valo return;
20e705c121SKalle Valo
21e705c121SKalle Valo IWL_ERR(mvm, "Enter CT Kill\n");
22e705c121SKalle Valo iwl_mvm_set_hw_ctkill_state(mvm, true);
23e705c121SKalle Valo
24c221daf2SChaya Rachel Ivgi if (!iwl_mvm_is_tt_in_fw(mvm)) {
25e705c121SKalle Valo tt->throttle = false;
26e705c121SKalle Valo tt->dynamic_smps = false;
27c221daf2SChaya Rachel Ivgi }
28e705c121SKalle Valo
29e705c121SKalle Valo /* Don't schedule an exit work if we're in test mode, since
30e705c121SKalle Valo * the temperature will not change unless we manually set it
31e705c121SKalle Valo * again (or disable testing).
32e705c121SKalle Valo */
33e705c121SKalle Valo if (!mvm->temperature_test)
34e705c121SKalle Valo schedule_delayed_work(&tt->ct_kill_exit,
35e705c121SKalle Valo round_jiffies_relative(duration * HZ));
36e705c121SKalle Valo }
37e705c121SKalle Valo
iwl_mvm_exit_ctkill(struct iwl_mvm * mvm)38e705c121SKalle Valo static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm)
39e705c121SKalle Valo {
40e705c121SKalle Valo if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
41e705c121SKalle Valo return;
42e705c121SKalle Valo
43e705c121SKalle Valo IWL_ERR(mvm, "Exit CT Kill\n");
44e705c121SKalle Valo iwl_mvm_set_hw_ctkill_state(mvm, false);
45e705c121SKalle Valo }
46e705c121SKalle Valo
iwl_mvm_tt_temp_changed(struct iwl_mvm * mvm,u32 temp)47d8367b12SJohannes Berg static void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp)
48e705c121SKalle Valo {
49e705c121SKalle Valo /* ignore the notification if we are in test mode */
50e705c121SKalle Valo if (mvm->temperature_test)
51e705c121SKalle Valo return;
52e705c121SKalle Valo
53e705c121SKalle Valo if (mvm->temperature == temp)
54e705c121SKalle Valo return;
55e705c121SKalle Valo
56e705c121SKalle Valo mvm->temperature = temp;
57e705c121SKalle Valo iwl_mvm_tt_handler(mvm);
58e705c121SKalle Valo }
59e705c121SKalle Valo
iwl_mvm_temp_notif_parse(struct iwl_mvm * mvm,struct iwl_rx_packet * pkt)60e705c121SKalle Valo static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm,
61e705c121SKalle Valo struct iwl_rx_packet *pkt)
62e705c121SKalle Valo {
63c221daf2SChaya Rachel Ivgi struct iwl_dts_measurement_notif_v1 *notif_v1;
64e705c121SKalle Valo int len = iwl_rx_packet_payload_len(pkt);
65e705c121SKalle Valo int temp;
66e705c121SKalle Valo
67c221daf2SChaya Rachel Ivgi /* we can use notif_v1 only, because v2 only adds an additional
68c221daf2SChaya Rachel Ivgi * parameter, which is not used in this function.
69c221daf2SChaya Rachel Ivgi */
70c221daf2SChaya Rachel Ivgi if (WARN_ON_ONCE(len < sizeof(*notif_v1))) {
71e705c121SKalle Valo IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
72e705c121SKalle Valo return -EINVAL;
73e705c121SKalle Valo }
74e705c121SKalle Valo
75c221daf2SChaya Rachel Ivgi notif_v1 = (void *)pkt->data;
76e705c121SKalle Valo
77c221daf2SChaya Rachel Ivgi temp = le32_to_cpu(notif_v1->temp);
78e705c121SKalle Valo
79e705c121SKalle Valo /* shouldn't be negative, but since it's s32, make sure it isn't */
80e705c121SKalle Valo if (WARN_ON_ONCE(temp < 0))
81e705c121SKalle Valo temp = 0;
82e705c121SKalle Valo
83e705c121SKalle Valo IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", temp);
84e705c121SKalle Valo
85e705c121SKalle Valo return temp;
86e705c121SKalle Valo }
87e705c121SKalle Valo
iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data * notif_wait,struct iwl_rx_packet * pkt,void * data)88e705c121SKalle Valo static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait,
89e705c121SKalle Valo struct iwl_rx_packet *pkt, void *data)
90e705c121SKalle Valo {
91e705c121SKalle Valo struct iwl_mvm *mvm =
92e705c121SKalle Valo container_of(notif_wait, struct iwl_mvm, notif_wait);
93e705c121SKalle Valo int *temp = data;
94e705c121SKalle Valo int ret;
95e705c121SKalle Valo
96e705c121SKalle Valo ret = iwl_mvm_temp_notif_parse(mvm, pkt);
97e705c121SKalle Valo if (ret < 0)
98e705c121SKalle Valo return true;
99e705c121SKalle Valo
100e705c121SKalle Valo *temp = ret;
101e705c121SKalle Valo
102e705c121SKalle Valo return true;
103e705c121SKalle Valo }
104e705c121SKalle Valo
iwl_mvm_temp_notif(struct iwl_mvm * mvm,struct iwl_rx_cmd_buffer * rxb)105e705c121SKalle Valo void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
106e705c121SKalle Valo {
107e705c121SKalle Valo struct iwl_rx_packet *pkt = rxb_addr(rxb);
108c221daf2SChaya Rachel Ivgi struct iwl_dts_measurement_notif_v2 *notif_v2;
109c221daf2SChaya Rachel Ivgi int len = iwl_rx_packet_payload_len(pkt);
110e705c121SKalle Valo int temp;
111c221daf2SChaya Rachel Ivgi u32 ths_crossed;
112e705c121SKalle Valo
113e705c121SKalle Valo /* the notification is handled synchronously in ctkill, so skip here */
114e705c121SKalle Valo if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
115e705c121SKalle Valo return;
116e705c121SKalle Valo
117e705c121SKalle Valo temp = iwl_mvm_temp_notif_parse(mvm, pkt);
118c221daf2SChaya Rachel Ivgi
119c221daf2SChaya Rachel Ivgi if (!iwl_mvm_is_tt_in_fw(mvm)) {
120c221daf2SChaya Rachel Ivgi if (temp >= 0)
121c221daf2SChaya Rachel Ivgi iwl_mvm_tt_temp_changed(mvm, temp);
122c221daf2SChaya Rachel Ivgi return;
123c221daf2SChaya Rachel Ivgi }
124c221daf2SChaya Rachel Ivgi
125c221daf2SChaya Rachel Ivgi if (WARN_ON_ONCE(len < sizeof(*notif_v2))) {
126c221daf2SChaya Rachel Ivgi IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
127c221daf2SChaya Rachel Ivgi return;
128c221daf2SChaya Rachel Ivgi }
129c221daf2SChaya Rachel Ivgi
130c221daf2SChaya Rachel Ivgi notif_v2 = (void *)pkt->data;
131c221daf2SChaya Rachel Ivgi ths_crossed = le32_to_cpu(notif_v2->threshold_idx);
132c221daf2SChaya Rachel Ivgi
133c221daf2SChaya Rachel Ivgi /* 0xFF in ths_crossed means the notification is not related
134c221daf2SChaya Rachel Ivgi * to a trip, so we can ignore it here.
135c221daf2SChaya Rachel Ivgi */
136c221daf2SChaya Rachel Ivgi if (ths_crossed == 0xFF)
137e705c121SKalle Valo return;
138e705c121SKalle Valo
139c221daf2SChaya Rachel Ivgi IWL_DEBUG_TEMP(mvm, "Temp = %d Threshold crossed = %d\n",
140c221daf2SChaya Rachel Ivgi temp, ths_crossed);
141c221daf2SChaya Rachel Ivgi
142c221daf2SChaya Rachel Ivgi #ifdef CONFIG_THERMAL
143c221daf2SChaya Rachel Ivgi if (WARN_ON(ths_crossed >= IWL_MAX_DTS_TRIPS))
144c221daf2SChaya Rachel Ivgi return;
145c221daf2SChaya Rachel Ivgi
14691f66a3cSEmmanuel Grumbach if (mvm->tz_device.tzone) {
14791f66a3cSEmmanuel Grumbach struct iwl_mvm_thermal_device *tz_dev = &mvm->tz_device;
14891f66a3cSEmmanuel Grumbach
14993effd83SThara Gopinath thermal_zone_device_update(tz_dev->tzone,
15093effd83SThara Gopinath THERMAL_TRIP_VIOLATED);
15191f66a3cSEmmanuel Grumbach }
152c221daf2SChaya Rachel Ivgi #endif /* CONFIG_THERMAL */
153e705c121SKalle Valo }
154e705c121SKalle Valo
iwl_mvm_ct_kill_notif(struct iwl_mvm * mvm,struct iwl_rx_cmd_buffer * rxb)1550a3b7119SChaya Rachel Ivgi void iwl_mvm_ct_kill_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
1560a3b7119SChaya Rachel Ivgi {
1570a3b7119SChaya Rachel Ivgi struct iwl_rx_packet *pkt = rxb_addr(rxb);
1580a3b7119SChaya Rachel Ivgi struct ct_kill_notif *notif;
1590a3b7119SChaya Rachel Ivgi
1600a3b7119SChaya Rachel Ivgi notif = (struct ct_kill_notif *)pkt->data;
1610a3b7119SChaya Rachel Ivgi IWL_DEBUG_TEMP(mvm, "CT Kill notification temperature = %d\n",
1620a3b7119SChaya Rachel Ivgi notif->temperature);
1635c7fd9dcSMiri Korenblit if (iwl_fw_lookup_notif_ver(mvm->fw, PHY_OPS_GROUP,
1645c7fd9dcSMiri Korenblit CT_KILL_NOTIFICATION, 0) > 1)
1655c7fd9dcSMiri Korenblit IWL_DEBUG_TEMP(mvm,
1665c7fd9dcSMiri Korenblit "CT kill notification DTS bitmap = 0x%x, Scheme = %d\n",
1675c7fd9dcSMiri Korenblit notif->dts, notif->scheme);
1680a3b7119SChaya Rachel Ivgi
1690a3b7119SChaya Rachel Ivgi iwl_mvm_enter_ctkill(mvm);
1700a3b7119SChaya Rachel Ivgi }
1710a3b7119SChaya Rachel Ivgi
172762c523fSGil Adam /*
173762c523fSGil Adam * send the DTS_MEASUREMENT_TRIGGER command with or without waiting for a
174762c523fSGil Adam * response. If we get a response then the measurement is stored in 'temp'
175762c523fSGil Adam */
iwl_mvm_send_temp_cmd(struct iwl_mvm * mvm,bool response,s32 * temp)176762c523fSGil Adam static int iwl_mvm_send_temp_cmd(struct iwl_mvm *mvm, bool response, s32 *temp)
177e705c121SKalle Valo {
178762c523fSGil Adam struct iwl_host_cmd cmd = {};
179762c523fSGil Adam struct iwl_dts_measurement_cmd dts_cmd = {
180e705c121SKalle Valo .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP),
181e705c121SKalle Valo };
182762c523fSGil Adam struct iwl_ext_dts_measurement_cmd ext_cmd = {
1836bd5fa33SGolan Ben Ami .control_mode = cpu_to_le32(DTS_DIRECT_WITHOUT_MEASURE),
184e705c121SKalle Valo };
185762c523fSGil Adam struct iwl_dts_measurement_resp *resp;
186762c523fSGil Adam void *cmd_ptr;
187762c523fSGil Adam int ret;
188762c523fSGil Adam u32 cmd_flags = 0;
189762c523fSGil Adam u16 len;
190e705c121SKalle Valo
191762c523fSGil Adam /* Check which command format is used (regular/extended) */
192762c523fSGil Adam if (fw_has_capa(&mvm->fw->ucode_capa,
193762c523fSGil Adam IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE)) {
194762c523fSGil Adam len = sizeof(ext_cmd);
195762c523fSGil Adam cmd_ptr = &ext_cmd;
196762c523fSGil Adam } else {
197762c523fSGil Adam len = sizeof(dts_cmd);
198762c523fSGil Adam cmd_ptr = &dts_cmd;
199762c523fSGil Adam }
200762c523fSGil Adam /* The command version where we get a response is zero length */
201762c523fSGil Adam if (response) {
202762c523fSGil Adam cmd_flags = CMD_WANT_SKB;
203762c523fSGil Adam len = 0;
204762c523fSGil Adam }
205e705c121SKalle Valo
206762c523fSGil Adam cmd.id = WIDE_ID(PHY_OPS_GROUP, CMD_DTS_MEASUREMENT_TRIGGER_WIDE);
207762c523fSGil Adam cmd.len[0] = len;
208762c523fSGil Adam cmd.flags = cmd_flags;
209762c523fSGil Adam cmd.data[0] = cmd_ptr;
210e705c121SKalle Valo
211762c523fSGil Adam IWL_DEBUG_TEMP(mvm,
212762c523fSGil Adam "Sending temperature measurement command - %s response\n",
213762c523fSGil Adam response ? "with" : "without");
214762c523fSGil Adam ret = iwl_mvm_send_cmd(mvm, &cmd);
215762c523fSGil Adam
216762c523fSGil Adam if (ret) {
217762c523fSGil Adam IWL_ERR(mvm,
218762c523fSGil Adam "Failed to send the temperature measurement command (err=%d)\n",
219762c523fSGil Adam ret);
220762c523fSGil Adam return ret;
221762c523fSGil Adam }
222762c523fSGil Adam
223762c523fSGil Adam if (response) {
224762c523fSGil Adam resp = (void *)cmd.resp_pkt->data;
225762c523fSGil Adam *temp = le32_to_cpu(resp->temp);
226762c523fSGil Adam IWL_DEBUG_TEMP(mvm,
227762c523fSGil Adam "Got temperature measurement response: temp=%d\n",
228762c523fSGil Adam *temp);
229762c523fSGil Adam iwl_free_resp(&cmd);
230762c523fSGil Adam }
231762c523fSGil Adam
232762c523fSGil Adam return ret;
233e705c121SKalle Valo }
234e705c121SKalle Valo
iwl_mvm_get_temp(struct iwl_mvm * mvm,s32 * temp)2357869318eSChaya Rachel Ivgi int iwl_mvm_get_temp(struct iwl_mvm *mvm, s32 *temp)
236e705c121SKalle Valo {
237e705c121SKalle Valo struct iwl_notification_wait wait_temp_notif;
238e705c121SKalle Valo static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP,
239e705c121SKalle Valo DTS_MEASUREMENT_NOTIF_WIDE) };
2407869318eSChaya Rachel Ivgi int ret;
241762c523fSGil Adam u8 cmd_ver;
242762c523fSGil Adam
243762c523fSGil Adam /*
244762c523fSGil Adam * If command version is 1 we send the command and immediately get
245762c523fSGil Adam * a response. For older versions we send the command and wait for a
246762c523fSGil Adam * notification (no command TLV for previous versions).
247762c523fSGil Adam */
248971cbe50SJohannes Berg cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
249971cbe50SJohannes Berg WIDE_ID(PHY_OPS_GROUP, CMD_DTS_MEASUREMENT_TRIGGER_WIDE),
250762c523fSGil Adam IWL_FW_CMD_VER_UNKNOWN);
251762c523fSGil Adam if (cmd_ver == 1)
252762c523fSGil Adam return iwl_mvm_send_temp_cmd(mvm, true, temp);
253e705c121SKalle Valo
254e705c121SKalle Valo lockdep_assert_held(&mvm->mutex);
255e705c121SKalle Valo
256e705c121SKalle Valo iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif,
257e705c121SKalle Valo temp_notif, ARRAY_SIZE(temp_notif),
2587869318eSChaya Rachel Ivgi iwl_mvm_temp_notif_wait, temp);
259e705c121SKalle Valo
260762c523fSGil Adam ret = iwl_mvm_send_temp_cmd(mvm, false, temp);
261e705c121SKalle Valo if (ret) {
262e705c121SKalle Valo iwl_remove_notification(&mvm->notif_wait, &wait_temp_notif);
263e705c121SKalle Valo return ret;
264e705c121SKalle Valo }
265e705c121SKalle Valo
266e705c121SKalle Valo ret = iwl_wait_notification(&mvm->notif_wait, &wait_temp_notif,
267e705c121SKalle Valo IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT);
2687869318eSChaya Rachel Ivgi if (ret)
269c61734a6SGolan Ben Ami IWL_WARN(mvm, "Getting the temperature timed out\n");
270e705c121SKalle Valo
2717869318eSChaya Rachel Ivgi return ret;
272e705c121SKalle Valo }
273e705c121SKalle Valo
check_exit_ctkill(struct work_struct * work)274e705c121SKalle Valo static void check_exit_ctkill(struct work_struct *work)
275e705c121SKalle Valo {
276e705c121SKalle Valo struct iwl_mvm_tt_mgmt *tt;
277e705c121SKalle Valo struct iwl_mvm *mvm;
278e705c121SKalle Valo u32 duration;
279e705c121SKalle Valo s32 temp;
2807869318eSChaya Rachel Ivgi int ret;
281e705c121SKalle Valo
282e705c121SKalle Valo tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work);
283e705c121SKalle Valo mvm = container_of(tt, struct iwl_mvm, thermal_throttle);
284e705c121SKalle Valo
2850a3b7119SChaya Rachel Ivgi if (iwl_mvm_is_tt_in_fw(mvm)) {
2860a3b7119SChaya Rachel Ivgi iwl_mvm_exit_ctkill(mvm);
2870a3b7119SChaya Rachel Ivgi
2880a3b7119SChaya Rachel Ivgi return;
2890a3b7119SChaya Rachel Ivgi }
2900a3b7119SChaya Rachel Ivgi
291e705c121SKalle Valo duration = tt->params.ct_kill_duration;
292e705c121SKalle Valo
293f9084775SNathan Errera flush_work(&mvm->roc_done_wk);
294f9084775SNathan Errera
295e705c121SKalle Valo mutex_lock(&mvm->mutex);
296e705c121SKalle Valo
297e705c121SKalle Valo if (__iwl_mvm_mac_start(mvm))
298e705c121SKalle Valo goto reschedule;
299e705c121SKalle Valo
3007869318eSChaya Rachel Ivgi ret = iwl_mvm_get_temp(mvm, &temp);
301e705c121SKalle Valo
302e705c121SKalle Valo __iwl_mvm_mac_stop(mvm);
303e705c121SKalle Valo
3047869318eSChaya Rachel Ivgi if (ret)
305e705c121SKalle Valo goto reschedule;
306e705c121SKalle Valo
307e705c121SKalle Valo IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp);
308e705c121SKalle Valo
309e705c121SKalle Valo if (temp <= tt->params.ct_kill_exit) {
310e705c121SKalle Valo mutex_unlock(&mvm->mutex);
311e705c121SKalle Valo iwl_mvm_exit_ctkill(mvm);
312e705c121SKalle Valo return;
313e705c121SKalle Valo }
314e705c121SKalle Valo
315e705c121SKalle Valo reschedule:
316e705c121SKalle Valo mutex_unlock(&mvm->mutex);
317e705c121SKalle Valo schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit,
318e705c121SKalle Valo round_jiffies(duration * HZ));
319e705c121SKalle Valo }
320e705c121SKalle Valo
iwl_mvm_tt_smps_iterator(void * _data,u8 * mac,struct ieee80211_vif * vif)321e705c121SKalle Valo static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac,
322e705c121SKalle Valo struct ieee80211_vif *vif)
323e705c121SKalle Valo {
324e705c121SKalle Valo struct iwl_mvm *mvm = _data;
325e705c121SKalle Valo enum ieee80211_smps_mode smps_mode;
326e705c121SKalle Valo
327e705c121SKalle Valo lockdep_assert_held(&mvm->mutex);
328e705c121SKalle Valo
329e705c121SKalle Valo if (mvm->thermal_throttle.dynamic_smps)
330e705c121SKalle Valo smps_mode = IEEE80211_SMPS_DYNAMIC;
331e705c121SKalle Valo else
332e705c121SKalle Valo smps_mode = IEEE80211_SMPS_AUTOMATIC;
333e705c121SKalle Valo
334e705c121SKalle Valo if (vif->type != NL80211_IFTYPE_STATION)
335e705c121SKalle Valo return;
336e705c121SKalle Valo
337*1a3e7039SGregory Greenman iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode, 0);
338e705c121SKalle Valo }
339e705c121SKalle Valo
iwl_mvm_tt_tx_protection(struct iwl_mvm * mvm,bool enable)340e705c121SKalle Valo static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable)
341e705c121SKalle Valo {
342e705c121SKalle Valo struct iwl_mvm_sta *mvmsta;
343e705c121SKalle Valo int i, err;
344e705c121SKalle Valo
345be9ae34eSNathan Errera for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
34613303c0fSSara Sharon mvmsta = iwl_mvm_sta_from_staid_protected(mvm, i);
34713303c0fSSara Sharon if (!mvmsta)
348e705c121SKalle Valo continue;
34913303c0fSSara Sharon
350e705c121SKalle Valo if (enable == mvmsta->tt_tx_protection)
351e705c121SKalle Valo continue;
352e705c121SKalle Valo err = iwl_mvm_tx_protection(mvm, mvmsta, enable);
353e705c121SKalle Valo if (err) {
354e705c121SKalle Valo IWL_ERR(mvm, "Failed to %s Tx protection\n",
355e705c121SKalle Valo enable ? "enable" : "disable");
356e705c121SKalle Valo } else {
357e705c121SKalle Valo IWL_DEBUG_TEMP(mvm, "%s Tx protection\n",
358e705c121SKalle Valo enable ? "Enable" : "Disable");
359e705c121SKalle Valo mvmsta->tt_tx_protection = enable;
360e705c121SKalle Valo }
361e705c121SKalle Valo }
362e705c121SKalle Valo }
363e705c121SKalle Valo
iwl_mvm_tt_tx_backoff(struct iwl_mvm * mvm,u32 backoff)364e705c121SKalle Valo void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
365e705c121SKalle Valo {
366e705c121SKalle Valo struct iwl_host_cmd cmd = {
367e705c121SKalle Valo .id = REPLY_THERMAL_MNG_BACKOFF,
368e705c121SKalle Valo .len = { sizeof(u32), },
369e705c121SKalle Valo .data = { &backoff, },
370e705c121SKalle Valo };
371e705c121SKalle Valo
372e705c121SKalle Valo backoff = max(backoff, mvm->thermal_throttle.min_backoff);
373e705c121SKalle Valo
374e705c121SKalle Valo if (iwl_mvm_send_cmd(mvm, &cmd) == 0) {
375e705c121SKalle Valo IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n",
376e705c121SKalle Valo backoff);
377e705c121SKalle Valo mvm->thermal_throttle.tx_backoff = backoff;
378e705c121SKalle Valo } else {
379e705c121SKalle Valo IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n");
380e705c121SKalle Valo }
381e705c121SKalle Valo }
382e705c121SKalle Valo
iwl_mvm_tt_handler(struct iwl_mvm * mvm)383e705c121SKalle Valo void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
384e705c121SKalle Valo {
385e705c121SKalle Valo struct iwl_tt_params *params = &mvm->thermal_throttle.params;
386e705c121SKalle Valo struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
387e705c121SKalle Valo s32 temperature = mvm->temperature;
388e705c121SKalle Valo bool throttle_enable = false;
389e705c121SKalle Valo int i;
390e705c121SKalle Valo u32 tx_backoff;
391e705c121SKalle Valo
392e705c121SKalle Valo IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature);
393e705c121SKalle Valo
394e705c121SKalle Valo if (params->support_ct_kill && temperature >= params->ct_kill_entry) {
395e705c121SKalle Valo iwl_mvm_enter_ctkill(mvm);
396e705c121SKalle Valo return;
397e705c121SKalle Valo }
398e705c121SKalle Valo
399e705c121SKalle Valo if (params->support_ct_kill &&
400e705c121SKalle Valo temperature <= params->ct_kill_exit) {
401e705c121SKalle Valo iwl_mvm_exit_ctkill(mvm);
402e705c121SKalle Valo return;
403e705c121SKalle Valo }
404e705c121SKalle Valo
405e705c121SKalle Valo if (params->support_dynamic_smps) {
406e705c121SKalle Valo if (!tt->dynamic_smps &&
407e705c121SKalle Valo temperature >= params->dynamic_smps_entry) {
408e705c121SKalle Valo IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n");
409e705c121SKalle Valo tt->dynamic_smps = true;
410e705c121SKalle Valo ieee80211_iterate_active_interfaces_atomic(
411e705c121SKalle Valo mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
412e705c121SKalle Valo iwl_mvm_tt_smps_iterator, mvm);
413e705c121SKalle Valo throttle_enable = true;
414e705c121SKalle Valo } else if (tt->dynamic_smps &&
415e705c121SKalle Valo temperature <= params->dynamic_smps_exit) {
416e705c121SKalle Valo IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n");
417e705c121SKalle Valo tt->dynamic_smps = false;
418e705c121SKalle Valo ieee80211_iterate_active_interfaces_atomic(
419e705c121SKalle Valo mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
420e705c121SKalle Valo iwl_mvm_tt_smps_iterator, mvm);
421e705c121SKalle Valo }
422e705c121SKalle Valo }
423e705c121SKalle Valo
424e705c121SKalle Valo if (params->support_tx_protection) {
425e705c121SKalle Valo if (temperature >= params->tx_protection_entry) {
426e705c121SKalle Valo iwl_mvm_tt_tx_protection(mvm, true);
427e705c121SKalle Valo throttle_enable = true;
428e705c121SKalle Valo } else if (temperature <= params->tx_protection_exit) {
429e705c121SKalle Valo iwl_mvm_tt_tx_protection(mvm, false);
430e705c121SKalle Valo }
431e705c121SKalle Valo }
432e705c121SKalle Valo
433e705c121SKalle Valo if (params->support_tx_backoff) {
434e705c121SKalle Valo tx_backoff = tt->min_backoff;
435e705c121SKalle Valo for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) {
436e705c121SKalle Valo if (temperature < params->tx_backoff[i].temperature)
437e705c121SKalle Valo break;
438e705c121SKalle Valo tx_backoff = max(tt->min_backoff,
439e705c121SKalle Valo params->tx_backoff[i].backoff);
440e705c121SKalle Valo }
441e705c121SKalle Valo if (tx_backoff != tt->min_backoff)
442e705c121SKalle Valo throttle_enable = true;
443e705c121SKalle Valo if (tt->tx_backoff != tx_backoff)
444e705c121SKalle Valo iwl_mvm_tt_tx_backoff(mvm, tx_backoff);
445e705c121SKalle Valo }
446e705c121SKalle Valo
447e705c121SKalle Valo if (!tt->throttle && throttle_enable) {
448e705c121SKalle Valo IWL_WARN(mvm,
449e705c121SKalle Valo "Due to high temperature thermal throttling initiated\n");
450e705c121SKalle Valo tt->throttle = true;
451e705c121SKalle Valo } else if (tt->throttle && !tt->dynamic_smps &&
452e705c121SKalle Valo tt->tx_backoff == tt->min_backoff &&
453e705c121SKalle Valo temperature <= params->tx_protection_exit) {
454e705c121SKalle Valo IWL_WARN(mvm,
455e705c121SKalle Valo "Temperature is back to normal thermal throttling stopped\n");
456e705c121SKalle Valo tt->throttle = false;
457e705c121SKalle Valo }
458e705c121SKalle Valo }
459e705c121SKalle Valo
460e705c121SKalle Valo static const struct iwl_tt_params iwl_mvm_default_tt_params = {
461e705c121SKalle Valo .ct_kill_entry = 118,
462e705c121SKalle Valo .ct_kill_exit = 96,
463e705c121SKalle Valo .ct_kill_duration = 5,
464e705c121SKalle Valo .dynamic_smps_entry = 114,
465e705c121SKalle Valo .dynamic_smps_exit = 110,
466e705c121SKalle Valo .tx_protection_entry = 114,
467e705c121SKalle Valo .tx_protection_exit = 108,
468e705c121SKalle Valo .tx_backoff = {
469e705c121SKalle Valo {.temperature = 112, .backoff = 200},
470e705c121SKalle Valo {.temperature = 113, .backoff = 600},
471e705c121SKalle Valo {.temperature = 114, .backoff = 1200},
472e705c121SKalle Valo {.temperature = 115, .backoff = 2000},
473e705c121SKalle Valo {.temperature = 116, .backoff = 4000},
474e705c121SKalle Valo {.temperature = 117, .backoff = 10000},
475e705c121SKalle Valo },
476e705c121SKalle Valo .support_ct_kill = true,
477e705c121SKalle Valo .support_dynamic_smps = true,
478e705c121SKalle Valo .support_tx_protection = true,
479e705c121SKalle Valo .support_tx_backoff = true,
480e705c121SKalle Valo };
481e705c121SKalle Valo
482b358993bSChaya Rachel Ivgi /* budget in mWatt */
483b358993bSChaya Rachel Ivgi static const u32 iwl_mvm_cdev_budgets[] = {
484220089c7SMordechay Goodstein 2400, /* cooling state 0 */
485220089c7SMordechay Goodstein 2000, /* cooling state 1 */
486220089c7SMordechay Goodstein 1800, /* cooling state 2 */
487220089c7SMordechay Goodstein 1600, /* cooling state 3 */
488220089c7SMordechay Goodstein 1400, /* cooling state 4 */
489220089c7SMordechay Goodstein 1200, /* cooling state 5 */
490220089c7SMordechay Goodstein 1000, /* cooling state 6 */
491220089c7SMordechay Goodstein 900, /* cooling state 7 */
492220089c7SMordechay Goodstein 800, /* cooling state 8 */
493220089c7SMordechay Goodstein 700, /* cooling state 9 */
494220089c7SMordechay Goodstein 650, /* cooling state 10 */
495220089c7SMordechay Goodstein 600, /* cooling state 11 */
496220089c7SMordechay Goodstein 550, /* cooling state 12 */
497220089c7SMordechay Goodstein 500, /* cooling state 13 */
498220089c7SMordechay Goodstein 450, /* cooling state 14 */
499220089c7SMordechay Goodstein 400, /* cooling state 15 */
500220089c7SMordechay Goodstein 350, /* cooling state 16 */
501220089c7SMordechay Goodstein 300, /* cooling state 17 */
502220089c7SMordechay Goodstein 250, /* cooling state 18 */
503220089c7SMordechay Goodstein 200, /* cooling state 19 */
504220089c7SMordechay Goodstein 150, /* cooling state 20 */
505b358993bSChaya Rachel Ivgi };
506b358993bSChaya Rachel Ivgi
iwl_mvm_ctdp_command(struct iwl_mvm * mvm,u32 op,u32 state)507b358993bSChaya Rachel Ivgi int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state)
50800f481bdSChaya Rachel Ivgi {
50900f481bdSChaya Rachel Ivgi struct iwl_mvm_ctdp_cmd cmd = {
51000f481bdSChaya Rachel Ivgi .operation = cpu_to_le32(op),
511b358993bSChaya Rachel Ivgi .budget = cpu_to_le32(iwl_mvm_cdev_budgets[state]),
51200f481bdSChaya Rachel Ivgi .window_size = 0,
51300f481bdSChaya Rachel Ivgi };
51400f481bdSChaya Rachel Ivgi int ret;
51500f481bdSChaya Rachel Ivgi u32 status;
51600f481bdSChaya Rachel Ivgi
51700f481bdSChaya Rachel Ivgi lockdep_assert_held(&mvm->mutex);
51800f481bdSChaya Rachel Ivgi
519d460f1fbSLuca Coelho status = 0;
52000f481bdSChaya Rachel Ivgi ret = iwl_mvm_send_cmd_pdu_status(mvm, WIDE_ID(PHY_OPS_GROUP,
52100f481bdSChaya Rachel Ivgi CTDP_CONFIG_CMD),
52200f481bdSChaya Rachel Ivgi sizeof(cmd), &cmd, &status);
52300f481bdSChaya Rachel Ivgi
52400f481bdSChaya Rachel Ivgi if (ret) {
52500f481bdSChaya Rachel Ivgi IWL_ERR(mvm, "cTDP command failed (err=%d)\n", ret);
52600f481bdSChaya Rachel Ivgi return ret;
52700f481bdSChaya Rachel Ivgi }
52800f481bdSChaya Rachel Ivgi
52900f481bdSChaya Rachel Ivgi switch (op) {
53000f481bdSChaya Rachel Ivgi case CTDP_CMD_OPERATION_START:
53100f481bdSChaya Rachel Ivgi #ifdef CONFIG_THERMAL
532b358993bSChaya Rachel Ivgi mvm->cooling_dev.cur_state = state;
53300f481bdSChaya Rachel Ivgi #endif /* CONFIG_THERMAL */
53400f481bdSChaya Rachel Ivgi break;
53500f481bdSChaya Rachel Ivgi case CTDP_CMD_OPERATION_REPORT:
53600f481bdSChaya Rachel Ivgi IWL_DEBUG_TEMP(mvm, "cTDP avg energy in mWatt = %d\n", status);
53700f481bdSChaya Rachel Ivgi /* when the function is called with CTDP_CMD_OPERATION_REPORT
53800f481bdSChaya Rachel Ivgi * option the function should return the average budget value
53900f481bdSChaya Rachel Ivgi * that is received from the FW.
54000f481bdSChaya Rachel Ivgi * The budget can't be less or equal to 0, so it's possible
54100f481bdSChaya Rachel Ivgi * to distinguish between error values and budgets.
54200f481bdSChaya Rachel Ivgi */
54300f481bdSChaya Rachel Ivgi return status;
54400f481bdSChaya Rachel Ivgi case CTDP_CMD_OPERATION_STOP:
54500f481bdSChaya Rachel Ivgi IWL_DEBUG_TEMP(mvm, "cTDP stopped successfully\n");
54600f481bdSChaya Rachel Ivgi break;
54700f481bdSChaya Rachel Ivgi }
54800f481bdSChaya Rachel Ivgi
54900f481bdSChaya Rachel Ivgi return 0;
55000f481bdSChaya Rachel Ivgi }
55100f481bdSChaya Rachel Ivgi
552c221daf2SChaya Rachel Ivgi #ifdef CONFIG_THERMAL
compare_temps(const void * a,const void * b)553c221daf2SChaya Rachel Ivgi static int compare_temps(const void *a, const void *b)
554c221daf2SChaya Rachel Ivgi {
555c221daf2SChaya Rachel Ivgi return ((s16)le16_to_cpu(*(__le16 *)a) -
556c221daf2SChaya Rachel Ivgi (s16)le16_to_cpu(*(__le16 *)b));
557c221daf2SChaya Rachel Ivgi }
5582d88b2cfSJohannes Berg #endif
559c221daf2SChaya Rachel Ivgi
iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm * mvm)560c221daf2SChaya Rachel Ivgi int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
561c221daf2SChaya Rachel Ivgi {
562c221daf2SChaya Rachel Ivgi struct temp_report_ths_cmd cmd = {0};
5632d88b2cfSJohannes Berg int ret;
5642d88b2cfSJohannes Berg #ifdef CONFIG_THERMAL
5652d88b2cfSJohannes Berg int i, j, idx = 0;
566c221daf2SChaya Rachel Ivgi
567c221daf2SChaya Rachel Ivgi lockdep_assert_held(&mvm->mutex);
568c221daf2SChaya Rachel Ivgi
56991f66a3cSEmmanuel Grumbach if (!mvm->tz_device.tzone)
5702d88b2cfSJohannes Berg goto send;
57191f66a3cSEmmanuel Grumbach
572c221daf2SChaya Rachel Ivgi /* The driver holds array of temperature trips that are unsorted
573c221daf2SChaya Rachel Ivgi * and uncompressed, the FW should get it compressed and sorted
574c221daf2SChaya Rachel Ivgi */
575c221daf2SChaya Rachel Ivgi
5763d2f20adSDaniel Lezcano /* compress trips to cmd array, remove uninitialized values*/
57791f66a3cSEmmanuel Grumbach for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
5783d2f20adSDaniel Lezcano if (mvm->tz_device.trips[i].temperature != INT_MIN) {
579c221daf2SChaya Rachel Ivgi cmd.thresholds[idx++] =
5803d2f20adSDaniel Lezcano cpu_to_le16((s16)(mvm->tz_device.trips[i].temperature / 1000));
581c221daf2SChaya Rachel Ivgi }
58291f66a3cSEmmanuel Grumbach }
583c221daf2SChaya Rachel Ivgi cmd.num_temps = cpu_to_le32(idx);
584c221daf2SChaya Rachel Ivgi
585c221daf2SChaya Rachel Ivgi if (!idx)
586c221daf2SChaya Rachel Ivgi goto send;
587c221daf2SChaya Rachel Ivgi
588c221daf2SChaya Rachel Ivgi /*sort cmd array*/
589c221daf2SChaya Rachel Ivgi sort(cmd.thresholds, idx, sizeof(s16), compare_temps, NULL);
590c221daf2SChaya Rachel Ivgi
591c221daf2SChaya Rachel Ivgi /* we should save the indexes of trips because we sort
592c221daf2SChaya Rachel Ivgi * and compress the orginal array
593c221daf2SChaya Rachel Ivgi */
594c221daf2SChaya Rachel Ivgi for (i = 0; i < idx; i++) {
595c221daf2SChaya Rachel Ivgi for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) {
5963d2f20adSDaniel Lezcano if ((int)(le16_to_cpu(cmd.thresholds[i]) * 1000) ==
5973d2f20adSDaniel Lezcano mvm->tz_device.trips[j].temperature)
598c221daf2SChaya Rachel Ivgi mvm->tz_device.fw_trips_index[i] = j;
599c221daf2SChaya Rachel Ivgi }
600c221daf2SChaya Rachel Ivgi }
601c221daf2SChaya Rachel Ivgi
602c221daf2SChaya Rachel Ivgi send:
6032d88b2cfSJohannes Berg #endif
604c221daf2SChaya Rachel Ivgi ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP,
605c221daf2SChaya Rachel Ivgi TEMP_REPORTING_THRESHOLDS_CMD),
606c221daf2SChaya Rachel Ivgi 0, sizeof(cmd), &cmd);
607c221daf2SChaya Rachel Ivgi if (ret)
608c221daf2SChaya Rachel Ivgi IWL_ERR(mvm, "TEMP_REPORT_THS_CMD command failed (err=%d)\n",
609c221daf2SChaya Rachel Ivgi ret);
610c221daf2SChaya Rachel Ivgi
611c221daf2SChaya Rachel Ivgi return ret;
612c221daf2SChaya Rachel Ivgi }
613c221daf2SChaya Rachel Ivgi
6142d88b2cfSJohannes Berg #ifdef CONFIG_THERMAL
iwl_mvm_tzone_get_temp(struct thermal_zone_device * device,int * temperature)615c221daf2SChaya Rachel Ivgi static int iwl_mvm_tzone_get_temp(struct thermal_zone_device *device,
616c221daf2SChaya Rachel Ivgi int *temperature)
617c221daf2SChaya Rachel Ivgi {
6183d4e1badSDaniel Lezcano struct iwl_mvm *mvm = thermal_zone_device_priv(device);
619c221daf2SChaya Rachel Ivgi int ret;
620c221daf2SChaya Rachel Ivgi int temp;
621c221daf2SChaya Rachel Ivgi
622c221daf2SChaya Rachel Ivgi mutex_lock(&mvm->mutex);
623c221daf2SChaya Rachel Ivgi
624aab6930dSJohannes Berg if (!iwl_mvm_firmware_running(mvm) ||
625702e975dSJohannes Berg mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
6261442a9a9SLuca Coelho ret = -ENODATA;
627c221daf2SChaya Rachel Ivgi goto out;
628c221daf2SChaya Rachel Ivgi }
629c221daf2SChaya Rachel Ivgi
630c221daf2SChaya Rachel Ivgi ret = iwl_mvm_get_temp(mvm, &temp);
631c221daf2SChaya Rachel Ivgi if (ret)
632c221daf2SChaya Rachel Ivgi goto out;
633c221daf2SChaya Rachel Ivgi
634c221daf2SChaya Rachel Ivgi *temperature = temp * 1000;
635c221daf2SChaya Rachel Ivgi
636c221daf2SChaya Rachel Ivgi out:
637c221daf2SChaya Rachel Ivgi mutex_unlock(&mvm->mutex);
638c221daf2SChaya Rachel Ivgi return ret;
639c221daf2SChaya Rachel Ivgi }
640c221daf2SChaya Rachel Ivgi
iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device * device,int trip,int temp)641c221daf2SChaya Rachel Ivgi static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device,
642c221daf2SChaya Rachel Ivgi int trip, int temp)
643c221daf2SChaya Rachel Ivgi {
6443d4e1badSDaniel Lezcano struct iwl_mvm *mvm = thermal_zone_device_priv(device);
645c221daf2SChaya Rachel Ivgi struct iwl_mvm_thermal_device *tzone;
6463d2f20adSDaniel Lezcano int ret;
647c221daf2SChaya Rachel Ivgi
648c221daf2SChaya Rachel Ivgi mutex_lock(&mvm->mutex);
649c221daf2SChaya Rachel Ivgi
650aab6930dSJohannes Berg if (!iwl_mvm_firmware_running(mvm) ||
651702e975dSJohannes Berg mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
652c221daf2SChaya Rachel Ivgi ret = -EIO;
653c221daf2SChaya Rachel Ivgi goto out;
654c221daf2SChaya Rachel Ivgi }
655c221daf2SChaya Rachel Ivgi
656c221daf2SChaya Rachel Ivgi if ((temp / 1000) > S16_MAX) {
657c221daf2SChaya Rachel Ivgi ret = -EINVAL;
658c221daf2SChaya Rachel Ivgi goto out;
659c221daf2SChaya Rachel Ivgi }
660c221daf2SChaya Rachel Ivgi
661c221daf2SChaya Rachel Ivgi tzone = &mvm->tz_device;
662c221daf2SChaya Rachel Ivgi if (!tzone) {
663c221daf2SChaya Rachel Ivgi ret = -EIO;
664c221daf2SChaya Rachel Ivgi goto out;
665c221daf2SChaya Rachel Ivgi }
666c221daf2SChaya Rachel Ivgi
667c221daf2SChaya Rachel Ivgi ret = iwl_mvm_send_temp_report_ths_cmd(mvm);
668c221daf2SChaya Rachel Ivgi out:
669c221daf2SChaya Rachel Ivgi mutex_unlock(&mvm->mutex);
670c221daf2SChaya Rachel Ivgi return ret;
671c221daf2SChaya Rachel Ivgi }
672c221daf2SChaya Rachel Ivgi
673c221daf2SChaya Rachel Ivgi static struct thermal_zone_device_ops tzone_ops = {
674c221daf2SChaya Rachel Ivgi .get_temp = iwl_mvm_tzone_get_temp,
675c221daf2SChaya Rachel Ivgi .set_trip_temp = iwl_mvm_tzone_set_trip_temp,
676c221daf2SChaya Rachel Ivgi };
677c221daf2SChaya Rachel Ivgi
678c221daf2SChaya Rachel Ivgi /* make all trips writable */
679c221daf2SChaya Rachel Ivgi #define IWL_WRITABLE_TRIPS_MSK (BIT(IWL_MAX_DTS_TRIPS) - 1)
680c221daf2SChaya Rachel Ivgi
iwl_mvm_thermal_zone_register(struct iwl_mvm * mvm)681c221daf2SChaya Rachel Ivgi static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
682c221daf2SChaya Rachel Ivgi {
683bbcf90c0SAndrzej Pietrasiewicz int i, ret;
684baa6cf84SAndrei Otcheretianski char name[16];
685baa6cf84SAndrei Otcheretianski static atomic_t counter = ATOMIC_INIT(0);
686c221daf2SChaya Rachel Ivgi
687c221daf2SChaya Rachel Ivgi if (!iwl_mvm_is_tt_in_fw(mvm)) {
688c221daf2SChaya Rachel Ivgi mvm->tz_device.tzone = NULL;
689c221daf2SChaya Rachel Ivgi
690c221daf2SChaya Rachel Ivgi return;
691c221daf2SChaya Rachel Ivgi }
692c221daf2SChaya Rachel Ivgi
693c221daf2SChaya Rachel Ivgi BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
694c221daf2SChaya Rachel Ivgi
695baa6cf84SAndrei Otcheretianski sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF);
6963d2f20adSDaniel Lezcano mvm->tz_device.tzone = thermal_zone_device_register_with_trips(name,
6973d2f20adSDaniel Lezcano mvm->tz_device.trips,
698c221daf2SChaya Rachel Ivgi IWL_MAX_DTS_TRIPS,
699c221daf2SChaya Rachel Ivgi IWL_WRITABLE_TRIPS_MSK,
700c221daf2SChaya Rachel Ivgi mvm, &tzone_ops,
701c221daf2SChaya Rachel Ivgi NULL, 0, 0);
702c221daf2SChaya Rachel Ivgi if (IS_ERR(mvm->tz_device.tzone)) {
703c221daf2SChaya Rachel Ivgi IWL_DEBUG_TEMP(mvm,
704c221daf2SChaya Rachel Ivgi "Failed to register to thermal zone (err = %ld)\n",
705c221daf2SChaya Rachel Ivgi PTR_ERR(mvm->tz_device.tzone));
70691f66a3cSEmmanuel Grumbach mvm->tz_device.tzone = NULL;
707c221daf2SChaya Rachel Ivgi return;
708c221daf2SChaya Rachel Ivgi }
709c221daf2SChaya Rachel Ivgi
710bbcf90c0SAndrzej Pietrasiewicz ret = thermal_zone_device_enable(mvm->tz_device.tzone);
711bbcf90c0SAndrzej Pietrasiewicz if (ret) {
712bbcf90c0SAndrzej Pietrasiewicz IWL_DEBUG_TEMP(mvm, "Failed to enable thermal zone\n");
713bbcf90c0SAndrzej Pietrasiewicz thermal_zone_device_unregister(mvm->tz_device.tzone);
714bbcf90c0SAndrzej Pietrasiewicz return;
715bbcf90c0SAndrzej Pietrasiewicz }
716bbcf90c0SAndrzej Pietrasiewicz
717c221daf2SChaya Rachel Ivgi /* 0 is a valid temperature,
718c221daf2SChaya Rachel Ivgi * so initialize the array with S16_MIN which invalid temperature
719c221daf2SChaya Rachel Ivgi */
7203d2f20adSDaniel Lezcano for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++) {
7213d2f20adSDaniel Lezcano mvm->tz_device.trips[i].temperature = INT_MIN;
7223d2f20adSDaniel Lezcano mvm->tz_device.trips[i].type = THERMAL_TRIP_PASSIVE;
7233d2f20adSDaniel Lezcano }
724c221daf2SChaya Rachel Ivgi }
725c221daf2SChaya Rachel Ivgi
iwl_mvm_tcool_get_max_state(struct thermal_cooling_device * cdev,unsigned long * state)7265c89e7bcSChaya Rachel Ivgi static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev,
7275c89e7bcSChaya Rachel Ivgi unsigned long *state)
7285c89e7bcSChaya Rachel Ivgi {
7295c89e7bcSChaya Rachel Ivgi *state = ARRAY_SIZE(iwl_mvm_cdev_budgets) - 1;
7305c89e7bcSChaya Rachel Ivgi
7315c89e7bcSChaya Rachel Ivgi return 0;
7325c89e7bcSChaya Rachel Ivgi }
7335c89e7bcSChaya Rachel Ivgi
iwl_mvm_tcool_get_cur_state(struct thermal_cooling_device * cdev,unsigned long * state)7345c89e7bcSChaya Rachel Ivgi static int iwl_mvm_tcool_get_cur_state(struct thermal_cooling_device *cdev,
7355c89e7bcSChaya Rachel Ivgi unsigned long *state)
7365c89e7bcSChaya Rachel Ivgi {
7375c89e7bcSChaya Rachel Ivgi struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
7385c89e7bcSChaya Rachel Ivgi
7395c89e7bcSChaya Rachel Ivgi *state = mvm->cooling_dev.cur_state;
740b358993bSChaya Rachel Ivgi
7415c89e7bcSChaya Rachel Ivgi return 0;
7425c89e7bcSChaya Rachel Ivgi }
7435c89e7bcSChaya Rachel Ivgi
iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device * cdev,unsigned long new_state)7445c89e7bcSChaya Rachel Ivgi static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev,
7455c89e7bcSChaya Rachel Ivgi unsigned long new_state)
7465c89e7bcSChaya Rachel Ivgi {
7475c89e7bcSChaya Rachel Ivgi struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata);
7485c89e7bcSChaya Rachel Ivgi int ret;
7495c89e7bcSChaya Rachel Ivgi
7505c89e7bcSChaya Rachel Ivgi mutex_lock(&mvm->mutex);
7515c89e7bcSChaya Rachel Ivgi
752aab6930dSJohannes Berg if (!iwl_mvm_firmware_running(mvm) ||
753702e975dSJohannes Berg mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) {
754d9954405SJohannes Berg ret = -EIO;
755d9954405SJohannes Berg goto unlock;
756d9954405SJohannes Berg }
757d9954405SJohannes Berg
7585c89e7bcSChaya Rachel Ivgi if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets)) {
7595c89e7bcSChaya Rachel Ivgi ret = -EINVAL;
7605c89e7bcSChaya Rachel Ivgi goto unlock;
7615c89e7bcSChaya Rachel Ivgi }
7625c89e7bcSChaya Rachel Ivgi
7635c89e7bcSChaya Rachel Ivgi ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START,
764b358993bSChaya Rachel Ivgi new_state);
7655c89e7bcSChaya Rachel Ivgi
7665c89e7bcSChaya Rachel Ivgi unlock:
7675c89e7bcSChaya Rachel Ivgi mutex_unlock(&mvm->mutex);
7685c89e7bcSChaya Rachel Ivgi return ret;
7695c89e7bcSChaya Rachel Ivgi }
7705c89e7bcSChaya Rachel Ivgi
771b1a1efc5SBhumika Goyal static const struct thermal_cooling_device_ops tcooling_ops = {
7725c89e7bcSChaya Rachel Ivgi .get_max_state = iwl_mvm_tcool_get_max_state,
7735c89e7bcSChaya Rachel Ivgi .get_cur_state = iwl_mvm_tcool_get_cur_state,
7745c89e7bcSChaya Rachel Ivgi .set_cur_state = iwl_mvm_tcool_set_cur_state,
7755c89e7bcSChaya Rachel Ivgi };
7765c89e7bcSChaya Rachel Ivgi
iwl_mvm_cooling_device_register(struct iwl_mvm * mvm)77791f66a3cSEmmanuel Grumbach static void iwl_mvm_cooling_device_register(struct iwl_mvm *mvm)
7785c89e7bcSChaya Rachel Ivgi {
7795c89e7bcSChaya Rachel Ivgi char name[] = "iwlwifi";
7805c89e7bcSChaya Rachel Ivgi
78191f66a3cSEmmanuel Grumbach if (!iwl_mvm_is_ctdp_supported(mvm))
78291f66a3cSEmmanuel Grumbach return;
7835c89e7bcSChaya Rachel Ivgi
7845c89e7bcSChaya Rachel Ivgi BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
7855c89e7bcSChaya Rachel Ivgi
7865c89e7bcSChaya Rachel Ivgi mvm->cooling_dev.cdev =
7875c89e7bcSChaya Rachel Ivgi thermal_cooling_device_register(name,
7885c89e7bcSChaya Rachel Ivgi mvm,
7895c89e7bcSChaya Rachel Ivgi &tcooling_ops);
7905c89e7bcSChaya Rachel Ivgi
7915c89e7bcSChaya Rachel Ivgi if (IS_ERR(mvm->cooling_dev.cdev)) {
7925c89e7bcSChaya Rachel Ivgi IWL_DEBUG_TEMP(mvm,
7935c89e7bcSChaya Rachel Ivgi "Failed to register to cooling device (err = %ld)\n",
7945c89e7bcSChaya Rachel Ivgi PTR_ERR(mvm->cooling_dev.cdev));
79591f66a3cSEmmanuel Grumbach mvm->cooling_dev.cdev = NULL;
79691f66a3cSEmmanuel Grumbach return;
7975c89e7bcSChaya Rachel Ivgi }
7985c89e7bcSChaya Rachel Ivgi }
7995c89e7bcSChaya Rachel Ivgi
iwl_mvm_thermal_zone_unregister(struct iwl_mvm * mvm)800c221daf2SChaya Rachel Ivgi static void iwl_mvm_thermal_zone_unregister(struct iwl_mvm *mvm)
801c221daf2SChaya Rachel Ivgi {
80291f66a3cSEmmanuel Grumbach if (!iwl_mvm_is_tt_in_fw(mvm) || !mvm->tz_device.tzone)
803c221daf2SChaya Rachel Ivgi return;
804c221daf2SChaya Rachel Ivgi
805c221daf2SChaya Rachel Ivgi IWL_DEBUG_TEMP(mvm, "Thermal zone device unregister\n");
80692549cdcSJens Axboe if (mvm->tz_device.tzone) {
807c221daf2SChaya Rachel Ivgi thermal_zone_device_unregister(mvm->tz_device.tzone);
808c221daf2SChaya Rachel Ivgi mvm->tz_device.tzone = NULL;
809c221daf2SChaya Rachel Ivgi }
81092549cdcSJens Axboe }
8115c89e7bcSChaya Rachel Ivgi
iwl_mvm_cooling_device_unregister(struct iwl_mvm * mvm)8125c89e7bcSChaya Rachel Ivgi static void iwl_mvm_cooling_device_unregister(struct iwl_mvm *mvm)
8135c89e7bcSChaya Rachel Ivgi {
81491f66a3cSEmmanuel Grumbach if (!iwl_mvm_is_ctdp_supported(mvm) || !mvm->cooling_dev.cdev)
8155c89e7bcSChaya Rachel Ivgi return;
8165c89e7bcSChaya Rachel Ivgi
8175c89e7bcSChaya Rachel Ivgi IWL_DEBUG_TEMP(mvm, "Cooling device unregister\n");
81892549cdcSJens Axboe if (mvm->cooling_dev.cdev) {
8195c89e7bcSChaya Rachel Ivgi thermal_cooling_device_unregister(mvm->cooling_dev.cdev);
8205c89e7bcSChaya Rachel Ivgi mvm->cooling_dev.cdev = NULL;
8215c89e7bcSChaya Rachel Ivgi }
82292549cdcSJens Axboe }
823c221daf2SChaya Rachel Ivgi #endif /* CONFIG_THERMAL */
824c221daf2SChaya Rachel Ivgi
iwl_mvm_thermal_initialize(struct iwl_mvm * mvm,u32 min_backoff)825c221daf2SChaya Rachel Ivgi void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff)
826e705c121SKalle Valo {
827e705c121SKalle Valo struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
828e705c121SKalle Valo
829e705c121SKalle Valo IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
830e705c121SKalle Valo
831e705c121SKalle Valo if (mvm->cfg->thermal_params)
832e705c121SKalle Valo tt->params = *mvm->cfg->thermal_params;
833e705c121SKalle Valo else
834e705c121SKalle Valo tt->params = iwl_mvm_default_tt_params;
835e705c121SKalle Valo
836e705c121SKalle Valo tt->throttle = false;
837e705c121SKalle Valo tt->dynamic_smps = false;
838e705c121SKalle Valo tt->min_backoff = min_backoff;
839e705c121SKalle Valo INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
840c221daf2SChaya Rachel Ivgi
841c221daf2SChaya Rachel Ivgi #ifdef CONFIG_THERMAL
8425c89e7bcSChaya Rachel Ivgi iwl_mvm_cooling_device_register(mvm);
843c221daf2SChaya Rachel Ivgi iwl_mvm_thermal_zone_register(mvm);
844c221daf2SChaya Rachel Ivgi #endif
845de8ba41bSLiad Kaufman mvm->init_status |= IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
846e705c121SKalle Valo }
847e705c121SKalle Valo
iwl_mvm_thermal_exit(struct iwl_mvm * mvm)848c221daf2SChaya Rachel Ivgi void iwl_mvm_thermal_exit(struct iwl_mvm *mvm)
849e705c121SKalle Valo {
850de8ba41bSLiad Kaufman if (!(mvm->init_status & IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE))
851de8ba41bSLiad Kaufman return;
852de8ba41bSLiad Kaufman
853e705c121SKalle Valo cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
854e705c121SKalle Valo IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
855c221daf2SChaya Rachel Ivgi
856c221daf2SChaya Rachel Ivgi #ifdef CONFIG_THERMAL
8575c89e7bcSChaya Rachel Ivgi iwl_mvm_cooling_device_unregister(mvm);
858c221daf2SChaya Rachel Ivgi iwl_mvm_thermal_zone_unregister(mvm);
859c221daf2SChaya Rachel Ivgi #endif
860de8ba41bSLiad Kaufman mvm->init_status &= ~IWL_MVM_INIT_STATUS_THERMAL_INIT_COMPLETE;
861e705c121SKalle Valo }
862