aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/intel
diff options
context:
space:
mode:
authorChaya Rachel Ivgi <chaya.rachel.ivgi@intel.com>2015-12-29 02:54:49 -0500
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2016-02-27 14:59:49 -0500
commitc221daf219b1cf38e7c4307f0f420ea826678af5 (patch)
treeb0ca50346d363ffbbc894326918c06812d28f656 /drivers/net/wireless/intel
parent0a3b7119000d706dfbc7e0c5b66e192a646d365f (diff)
iwlwifi: mvm: add registration to thermal zone
Register to thermal_zone interface and implement the thermal ops. The thermal handles the device throttling, and sets the the temperature thresholds the Thermal Manager would be notified of crossing. The thermal interface adds a new thermal zone device sensor under /sys/class/thermal/ folder. Signed-off-by: Chaya Rachel Ivgi <chaya.rachel.ivgi@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com> Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Diffstat (limited to 'drivers/net/wireless/intel')
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h33
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw.c13
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mvm.h31
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ops.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tt.c287
5 files changed, 353 insertions, 17 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
index ecbf7cb600ce..e692098a9f1e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
@@ -279,6 +279,7 @@ enum {
279 */ 279 */
280enum iwl_phy_ops_subcmd_ids { 280enum iwl_phy_ops_subcmd_ids {
281 CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0, 281 CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0,
282 TEMP_REPORTING_THRESHOLDS_CMD = 0x04,
282 CT_KILL_NOTIFICATION = 0xFE, 283 CT_KILL_NOTIFICATION = 0xFE,
283 DTS_MEASUREMENT_NOTIF_WIDE = 0xFF, 284 DTS_MEASUREMENT_NOTIF_WIDE = 0xFF,
284}; 285};
@@ -1676,15 +1677,28 @@ struct iwl_ext_dts_measurement_cmd {
1676} __packed; /* XVT_FW_DTS_CONTROL_MEASUREMENT_REQUEST_API_S */ 1677} __packed; /* XVT_FW_DTS_CONTROL_MEASUREMENT_REQUEST_API_S */
1677 1678
1678/** 1679/**
1679 * iwl_dts_measurement_notif - notification received with the measurements 1680 * struct iwl_dts_measurement_notif_v1 - measurements notification
1680 * 1681 *
1681 * @temp: the measured temperature 1682 * @temp: the measured temperature
1682 * @voltage: the measured voltage 1683 * @voltage: the measured voltage
1683 */ 1684 */
1684struct iwl_dts_measurement_notif { 1685struct iwl_dts_measurement_notif_v1 {
1685 __le32 temp; 1686 __le32 temp;
1686 __le32 voltage; 1687 __le32 voltage;
1687} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S */ 1688} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S_VER_1*/
1689
1690/**
1691 * struct iwl_dts_measurement_notif_v2 - measurements notification
1692 *
1693 * @temp: the measured temperature
1694 * @voltage: the measured voltage
1695 * @threshold_idx: the trip index that was crossed
1696 */
1697struct iwl_dts_measurement_notif_v2 {
1698 __le32 temp;
1699 __le32 voltage;
1700 __le32 threshold_idx;
1701} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S_VER_2 */
1688 1702
1689/** 1703/**
1690 * struct ct_kill_notif - CT-kill entry notification 1704 * struct ct_kill_notif - CT-kill entry notification
@@ -1697,6 +1711,19 @@ struct ct_kill_notif {
1697 __le16 reserved; 1711 __le16 reserved;
1698} __packed; /* GRP_PHY_CT_KILL_NTF */ 1712} __packed; /* GRP_PHY_CT_KILL_NTF */
1699 1713
1714#define IWL_MAX_DTS_TRIPS 8
1715
1716/**
1717 * struct iwl_temp_report_ths_cmd - set temperature thresholds
1718 *
1719 * @num_temps: number of temperature thresholds passed
1720 * @thresholds: array with the thresholds to be configured
1721 */
1722struct temp_report_ths_cmd {
1723 __le32 num_temps;
1724 __le16 thresholds[IWL_MAX_DTS_TRIPS];
1725} __packed; /* GRP_PHY_TEMP_REPORTING_THRESHOLDS_CMD */
1726
1700/*********************************** 1727/***********************************
1701 * TDLS API 1728 * TDLS API
1702 ***********************************/ 1729 ***********************************/
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index 070e2af05ca2..07f2cbd9c8e7 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -952,8 +952,21 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
952 goto error; 952 goto error;
953 } 953 }
954 954
955#ifdef CONFIG_THERMAL
956 if (iwl_mvm_is_tt_in_fw(mvm)) {
957 /* in order to give the responsibility of ct-kill and
958 * TX backoff to FW we need to send empty temperature reporting
959 * cmd during init time
960 */
961 iwl_mvm_send_temp_report_ths_cmd(mvm);
962 } else {
963 /* Initialize tx backoffs to the minimal possible */
964 iwl_mvm_tt_tx_backoff(mvm, 0);
965 }
966#else
955 /* Initialize tx backoffs to the minimal possible */ 967 /* Initialize tx backoffs to the minimal possible */
956 iwl_mvm_tt_tx_backoff(mvm, 0); 968 iwl_mvm_tt_tx_backoff(mvm, 0);
969#endif
957 970
958 WARN_ON(iwl_mvm_config_ltr(mvm)); 971 WARN_ON(iwl_mvm_config_ltr(mvm));
959 972
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index 200bbb76ff0a..87d3e2884886 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -73,6 +73,10 @@
73#include <linux/leds.h> 73#include <linux/leds.h>
74#include <linux/in6.h> 74#include <linux/in6.h>
75 75
76#ifdef CONFIG_THERMAL
77#include <linux/thermal.h>
78#endif
79
76#include "iwl-op-mode.h" 80#include "iwl-op-mode.h"
77#include "iwl-trans.h" 81#include "iwl-trans.h"
78#include "iwl-notif-wait.h" 82#include "iwl-notif-wait.h"
@@ -519,6 +523,20 @@ struct iwl_mvm_tt_mgmt {
519 bool throttle; 523 bool throttle;
520}; 524};
521 525
526#ifdef CONFIG_THERMAL
527/**
528 *struct iwl_mvm_thermal_device - thermal zone related data
529 * @temp_trips: temperature thresholds for report
530 * @fw_trips_index: keep indexes to original array - temp_trips
531 * @tzone: thermal zone device data
532*/
533struct iwl_mvm_thermal_device {
534 s16 temp_trips[IWL_MAX_DTS_TRIPS];
535 u8 fw_trips_index[IWL_MAX_DTS_TRIPS];
536 struct thermal_zone_device *tzone;
537};
538#endif
539
522#define IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES 8 540#define IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES 8
523 541
524struct iwl_mvm_frame_stats { 542struct iwl_mvm_frame_stats {
@@ -799,6 +817,10 @@ struct iwl_mvm {
799 817
800 /* Thermal Throttling and CTkill */ 818 /* Thermal Throttling and CTkill */
801 struct iwl_mvm_tt_mgmt thermal_throttle; 819 struct iwl_mvm_tt_mgmt thermal_throttle;
820#ifdef CONFIG_THERMAL
821 struct iwl_mvm_thermal_device tz_device;
822#endif
823
802 s32 temperature; /* Celsius */ 824 s32 temperature; /* Celsius */
803 /* 825 /*
804 * Debug option to set the NIC temperature. This option makes the 826 * Debug option to set the NIC temperature. This option makes the
@@ -1032,6 +1054,7 @@ static inline bool iwl_mvm_has_new_rx_api(struct iwl_mvm *mvm)
1032 1054
1033static inline bool iwl_mvm_is_tt_in_fw(struct iwl_mvm *mvm) 1055static inline bool iwl_mvm_is_tt_in_fw(struct iwl_mvm *mvm)
1034{ 1056{
1057#ifdef CONFIG_THERMAL
1035 /* these two TLV are redundant since the responsibility to CT-kill by 1058 /* these two TLV are redundant since the responsibility to CT-kill by
1036 * FW happens only after we send at least one command of 1059 * FW happens only after we send at least one command of
1037 * temperature THs report. 1060 * temperature THs report.
@@ -1040,6 +1063,9 @@ static inline bool iwl_mvm_is_tt_in_fw(struct iwl_mvm *mvm)
1040 IWL_UCODE_TLV_CAPA_CT_KILL_BY_FW) && 1063 IWL_UCODE_TLV_CAPA_CT_KILL_BY_FW) &&
1041 fw_has_capa(&mvm->fw->ucode_capa, 1064 fw_has_capa(&mvm->fw->ucode_capa,
1042 IWL_UCODE_TLV_CAPA_TEMP_THS_REPORT_SUPPORT); 1065 IWL_UCODE_TLV_CAPA_TEMP_THS_REPORT_SUPPORT);
1066#else /* CONFIG_THERMAL */
1067 return false;
1068#endif /* CONFIG_THERMAL */
1043} 1069}
1044 1070
1045extern const u8 iwl_mvm_ac_to_tx_fifo[]; 1071extern const u8 iwl_mvm_ac_to_tx_fifo[];
@@ -1512,11 +1538,12 @@ void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp);
1512void iwl_mvm_temp_notif(struct iwl_mvm *mvm, 1538void iwl_mvm_temp_notif(struct iwl_mvm *mvm,
1513 struct iwl_rx_cmd_buffer *rxb); 1539 struct iwl_rx_cmd_buffer *rxb);
1514void iwl_mvm_tt_handler(struct iwl_mvm *mvm); 1540void iwl_mvm_tt_handler(struct iwl_mvm *mvm);
1515void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff); 1541void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff);
1516void iwl_mvm_tt_exit(struct iwl_mvm *mvm); 1542void iwl_mvm_thermal_exit(struct iwl_mvm *mvm);
1517void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); 1543void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
1518int iwl_mvm_get_temp(struct iwl_mvm *mvm, s32 *temp); 1544int iwl_mvm_get_temp(struct iwl_mvm *mvm, s32 *temp);
1519void iwl_mvm_ct_kill_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); 1545void iwl_mvm_ct_kill_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
1546int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm);
1520 1547
1521/* Location Aware Regulatory */ 1548/* Location Aware Regulatory */
1522struct iwl_mcc_update_resp * 1549struct iwl_mcc_update_resp *
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
index ecc371e1f3f0..a7acadd446c4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
@@ -389,6 +389,7 @@ static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = {
389 */ 389 */
390static const struct iwl_hcmd_names iwl_mvm_phy_names[] = { 390static const struct iwl_hcmd_names iwl_mvm_phy_names[] = {
391 HCMD_NAME(CMD_DTS_MEASUREMENT_TRIGGER_WIDE), 391 HCMD_NAME(CMD_DTS_MEASUREMENT_TRIGGER_WIDE),
392 HCMD_NAME(TEMP_REPORTING_THRESHOLDS_CMD),
392 HCMD_NAME(CT_KILL_NOTIFICATION), 393 HCMD_NAME(CT_KILL_NOTIFICATION),
393 HCMD_NAME(DTS_MEASUREMENT_NOTIF_WIDE), 394 HCMD_NAME(DTS_MEASUREMENT_NOTIF_WIDE),
394}; 395};
@@ -591,7 +592,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
591 mvm->cfg->name, mvm->trans->hw_rev); 592 mvm->cfg->name, mvm->trans->hw_rev);
592 593
593 min_backoff = calc_min_backoff(trans, cfg); 594 min_backoff = calc_min_backoff(trans, cfg);
594 iwl_mvm_tt_initialize(mvm, min_backoff); 595 iwl_mvm_thermal_initialize(mvm, min_backoff);
595 596
596 if (iwlwifi_mod_params.nvm_file) 597 if (iwlwifi_mod_params.nvm_file)
597 mvm->nvm_file_name = iwlwifi_mod_params.nvm_file; 598 mvm->nvm_file_name = iwlwifi_mod_params.nvm_file;
@@ -664,6 +665,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
664 out_unregister: 665 out_unregister:
665 ieee80211_unregister_hw(mvm->hw); 666 ieee80211_unregister_hw(mvm->hw);
666 iwl_mvm_leds_exit(mvm); 667 iwl_mvm_leds_exit(mvm);
668 iwl_mvm_thermal_exit(mvm);
667 out_free: 669 out_free:
668 flush_delayed_work(&mvm->fw_dump_wk); 670 flush_delayed_work(&mvm->fw_dump_wk);
669 iwl_phy_db_free(mvm->phy_db); 671 iwl_phy_db_free(mvm->phy_db);
@@ -681,7 +683,7 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
681 683
682 iwl_mvm_leds_exit(mvm); 684 iwl_mvm_leds_exit(mvm);
683 685
684 iwl_mvm_tt_exit(mvm); 686 iwl_mvm_thermal_exit(mvm);
685 687
686 ieee80211_unregister_hw(mvm->hw); 688 ieee80211_unregister_hw(mvm->hw);
687 689
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
index 6ba391099d7e..466d169b0e62 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
@@ -65,6 +65,8 @@
65 * 65 *
66 *****************************************************************************/ 66 *****************************************************************************/
67 67
68#include <linux/sort.h>
69
68#include "mvm.h" 70#include "mvm.h"
69 71
70#define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ 72#define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ
@@ -80,8 +82,10 @@ static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm)
80 IWL_ERR(mvm, "Enter CT Kill\n"); 82 IWL_ERR(mvm, "Enter CT Kill\n");
81 iwl_mvm_set_hw_ctkill_state(mvm, true); 83 iwl_mvm_set_hw_ctkill_state(mvm, true);
82 84
83 tt->throttle = false; 85 if (!iwl_mvm_is_tt_in_fw(mvm)) {
84 tt->dynamic_smps = false; 86 tt->throttle = false;
87 tt->dynamic_smps = false;
88 }
85 89
86 /* Don't schedule an exit work if we're in test mode, since 90 /* Don't schedule an exit work if we're in test mode, since
87 * the temperature will not change unless we manually set it 91 * the temperature will not change unless we manually set it
@@ -117,18 +121,21 @@ void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp)
117static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm, 121static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm,
118 struct iwl_rx_packet *pkt) 122 struct iwl_rx_packet *pkt)
119{ 123{
120 struct iwl_dts_measurement_notif *notif; 124 struct iwl_dts_measurement_notif_v1 *notif_v1;
121 int len = iwl_rx_packet_payload_len(pkt); 125 int len = iwl_rx_packet_payload_len(pkt);
122 int temp; 126 int temp;
123 127
124 if (WARN_ON_ONCE(len < sizeof(*notif))) { 128 /* we can use notif_v1 only, because v2 only adds an additional
129 * parameter, which is not used in this function.
130 */
131 if (WARN_ON_ONCE(len < sizeof(*notif_v1))) {
125 IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n"); 132 IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
126 return -EINVAL; 133 return -EINVAL;
127 } 134 }
128 135
129 notif = (void *)pkt->data; 136 notif_v1 = (void *)pkt->data;
130 137
131 temp = le32_to_cpu(notif->temp); 138 temp = le32_to_cpu(notif_v1->temp);
132 139
133 /* shouldn't be negative, but since it's s32, make sure it isn't */ 140 /* shouldn't be negative, but since it's s32, make sure it isn't */
134 if (WARN_ON_ONCE(temp < 0)) 141 if (WARN_ON_ONCE(temp < 0))
@@ -159,17 +166,56 @@ static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait,
159void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) 166void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
160{ 167{
161 struct iwl_rx_packet *pkt = rxb_addr(rxb); 168 struct iwl_rx_packet *pkt = rxb_addr(rxb);
169 struct iwl_dts_measurement_notif_v2 *notif_v2;
170 int len = iwl_rx_packet_payload_len(pkt);
162 int temp; 171 int temp;
172 u32 ths_crossed;
163 173
164 /* the notification is handled synchronously in ctkill, so skip here */ 174 /* the notification is handled synchronously in ctkill, so skip here */
165 if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) 175 if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
166 return; 176 return;
167 177
168 temp = iwl_mvm_temp_notif_parse(mvm, pkt); 178 temp = iwl_mvm_temp_notif_parse(mvm, pkt);
169 if (temp < 0) 179
180 if (!iwl_mvm_is_tt_in_fw(mvm)) {
181 if (temp >= 0)
182 iwl_mvm_tt_temp_changed(mvm, temp);
183 return;
184 }
185
186 if (WARN_ON_ONCE(len < sizeof(*notif_v2))) {
187 IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
188 return;
189 }
190
191 notif_v2 = (void *)pkt->data;
192 ths_crossed = le32_to_cpu(notif_v2->threshold_idx);
193
194 /* 0xFF in ths_crossed means the notification is not related
195 * to a trip, so we can ignore it here.
196 */
197 if (ths_crossed == 0xFF)
198 return;
199
200 IWL_DEBUG_TEMP(mvm, "Temp = %d Threshold crossed = %d\n",
201 temp, ths_crossed);
202
203#ifdef CONFIG_THERMAL
204 if (WARN_ON(ths_crossed >= IWL_MAX_DTS_TRIPS))
170 return; 205 return;
171 206
172 iwl_mvm_tt_temp_changed(mvm, temp); 207 /*
208 * We are now handling a temperature notification from the firmware
209 * in ASYNC and hold the mutex. thermal_notify_framework will call
210 * us back through get_temp() which ought to send a SYNC command to
211 * the firmware and hence to take the mutex.
212 * Avoid the deadlock by unlocking the mutex here.
213 */
214 mutex_unlock(&mvm->mutex);
215 thermal_notify_framework(mvm->tz_device.tzone,
216 mvm->tz_device.fw_trips_index[ths_crossed]);
217 mutex_lock(&mvm->mutex);
218#endif /* CONFIG_THERMAL */
173} 219}
174 220
175void iwl_mvm_ct_kill_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) 221void iwl_mvm_ct_kill_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
@@ -460,7 +506,220 @@ static const struct iwl_tt_params iwl_mvm_default_tt_params = {
460 .support_tx_backoff = true, 506 .support_tx_backoff = true,
461}; 507};
462 508
463void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff) 509#ifdef CONFIG_THERMAL
510static int compare_temps(const void *a, const void *b)
511{
512 return ((s16)le16_to_cpu(*(__le16 *)a) -
513 (s16)le16_to_cpu(*(__le16 *)b));
514}
515
516int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
517{
518 struct temp_report_ths_cmd cmd = {0};
519 int ret, i, j, idx = 0;
520
521 lockdep_assert_held(&mvm->mutex);
522
523 /* The driver holds array of temperature trips that are unsorted
524 * and uncompressed, the FW should get it compressed and sorted
525 */
526
527 /* compress temp_trips to cmd array, remove uninitialized values*/
528 for (i = 0; i < IWL_MAX_DTS_TRIPS; i++)
529 if (mvm->tz_device.temp_trips[i] != S16_MIN) {
530 cmd.thresholds[idx++] =
531 cpu_to_le16(mvm->tz_device.temp_trips[i]);
532 }
533 cmd.num_temps = cpu_to_le32(idx);
534
535 if (!idx)
536 goto send;
537
538 /*sort cmd array*/
539 sort(cmd.thresholds, idx, sizeof(s16), compare_temps, NULL);
540
541 /* we should save the indexes of trips because we sort
542 * and compress the orginal array
543 */
544 for (i = 0; i < idx; i++) {
545 for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) {
546 if (le16_to_cpu(cmd.thresholds[i]) ==
547 mvm->tz_device.temp_trips[j])
548 mvm->tz_device.fw_trips_index[i] = j;
549 }
550 }
551
552send:
553 ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP,
554 TEMP_REPORTING_THRESHOLDS_CMD),
555 0, sizeof(cmd), &cmd);
556 if (ret)
557 IWL_ERR(mvm, "TEMP_REPORT_THS_CMD command failed (err=%d)\n",
558 ret);
559
560 return ret;
561}
562
563static int iwl_mvm_tzone_get_temp(struct thermal_zone_device *device,
564 int *temperature)
565{
566 struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
567 int ret;
568 int temp;
569
570 mutex_lock(&mvm->mutex);
571
572 if (!mvm->ucode_loaded || !(mvm->cur_ucode == IWL_UCODE_REGULAR)) {
573 ret = -EIO;
574 goto out;
575 }
576
577 ret = iwl_mvm_get_temp(mvm, &temp);
578 if (ret)
579 goto out;
580
581 *temperature = temp * 1000;
582
583out:
584 mutex_unlock(&mvm->mutex);
585 return ret;
586}
587
588static int iwl_mvm_tzone_get_trip_temp(struct thermal_zone_device *device,
589 int trip, int *temp)
590{
591 struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
592
593 if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
594 return -EINVAL;
595
596 *temp = mvm->tz_device.temp_trips[trip] * 1000;
597
598 return 0;
599}
600
601static int iwl_mvm_tzone_get_trip_type(struct thermal_zone_device *device,
602 int trip, enum thermal_trip_type *type)
603{
604 if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS)
605 return -EINVAL;
606
607 *type = THERMAL_TRIP_PASSIVE;
608
609 return 0;
610}
611
612static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device,
613 int trip, int temp)
614{
615 struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata;
616 struct iwl_mvm_thermal_device *tzone;
617 int i, ret;
618 s16 temperature;
619
620 mutex_lock(&mvm->mutex);
621
622 if (!mvm->ucode_loaded || !(mvm->cur_ucode == IWL_UCODE_REGULAR)) {
623 ret = -EIO;
624 goto out;
625 }
626
627 if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS) {
628 ret = -EINVAL;
629 goto out;
630 }
631
632 if ((temp / 1000) > S16_MAX) {
633 ret = -EINVAL;
634 goto out;
635 }
636
637 temperature = (s16)(temp / 1000);
638 tzone = &mvm->tz_device;
639
640 if (!tzone) {
641 ret = -EIO;
642 goto out;
643 }
644
645 /* no updates*/
646 if (tzone->temp_trips[trip] == temperature) {
647 ret = 0;
648 goto out;
649 }
650
651 /* already existing temperature */
652 for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) {
653 if (tzone->temp_trips[i] == temperature) {
654 ret = -EINVAL;
655 goto out;
656 }
657 }
658
659 tzone->temp_trips[trip] = temperature;
660
661 ret = iwl_mvm_send_temp_report_ths_cmd(mvm);
662out:
663 mutex_unlock(&mvm->mutex);
664 return ret;
665}
666
667static struct thermal_zone_device_ops tzone_ops = {
668 .get_temp = iwl_mvm_tzone_get_temp,
669 .get_trip_temp = iwl_mvm_tzone_get_trip_temp,
670 .get_trip_type = iwl_mvm_tzone_get_trip_type,
671 .set_trip_temp = iwl_mvm_tzone_set_trip_temp,
672};
673
674/* make all trips writable */
675#define IWL_WRITABLE_TRIPS_MSK (BIT(IWL_MAX_DTS_TRIPS) - 1)
676
677static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm)
678{
679 int i;
680 char name[] = "iwlwifi";
681
682 if (!iwl_mvm_is_tt_in_fw(mvm)) {
683 mvm->tz_device.tzone = NULL;
684
685 return;
686 }
687
688 BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH);
689
690 mvm->tz_device.tzone = thermal_zone_device_register(name,
691 IWL_MAX_DTS_TRIPS,
692 IWL_WRITABLE_TRIPS_MSK,
693 mvm, &tzone_ops,
694 NULL, 0, 0);
695 if (IS_ERR(mvm->tz_device.tzone)) {
696 IWL_DEBUG_TEMP(mvm,
697 "Failed to register to thermal zone (err = %ld)\n",
698 PTR_ERR(mvm->tz_device.tzone));
699 return;
700 }
701
702 /* 0 is a valid temperature,
703 * so initialize the array with S16_MIN which invalid temperature
704 */
705 for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++)
706 mvm->tz_device.temp_trips[i] = S16_MIN;
707}
708
709static void iwl_mvm_thermal_zone_unregister(struct iwl_mvm *mvm)
710{
711 if (!iwl_mvm_is_tt_in_fw(mvm))
712 return;
713
714 if (mvm->tz_device.tzone) {
715 IWL_DEBUG_TEMP(mvm, "Thermal zone device unregister\n");
716 thermal_zone_device_unregister(mvm->tz_device.tzone);
717 mvm->tz_device.tzone = NULL;
718 }
719}
720#endif /* CONFIG_THERMAL */
721
722void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff)
464{ 723{
465 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; 724 struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
466 725
@@ -475,10 +734,18 @@ void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff)
475 tt->dynamic_smps = false; 734 tt->dynamic_smps = false;
476 tt->min_backoff = min_backoff; 735 tt->min_backoff = min_backoff;
477 INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill); 736 INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
737
738#ifdef CONFIG_THERMAL
739 iwl_mvm_thermal_zone_register(mvm);
740#endif
478} 741}
479 742
480void iwl_mvm_tt_exit(struct iwl_mvm *mvm) 743void iwl_mvm_thermal_exit(struct iwl_mvm *mvm)
481{ 744{
482 cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit); 745 cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit);
483 IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n"); 746 IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n");
747
748#ifdef CONFIG_THERMAL
749 iwl_mvm_thermal_zone_unregister(mvm);
750#endif
484} 751}