1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 /* 3 * Copyright (C) 2024-2025 Intel Corporation 4 */ 5 #ifdef CONFIG_THERMAL 6 #include <linux/sort.h> 7 #include <linux/thermal.h> 8 #endif 9 10 #include "fw/api/phy.h" 11 12 #include "thermal.h" 13 #include "mld.h" 14 #include "hcmd.h" 15 16 #define IWL_MLD_CT_KILL_DURATION (5 * HZ) 17 18 void iwl_mld_handle_ct_kill_notif(struct iwl_mld *mld, 19 struct iwl_rx_packet *pkt) 20 { 21 const struct ct_kill_notif *notif = (const void *)pkt->data; 22 23 IWL_ERR(mld, 24 "CT Kill notification: temp = %d, DTS = 0x%x, Scheme 0x%x - Enter CT Kill\n", 25 le16_to_cpu(notif->temperature), notif->dts, 26 notif->scheme); 27 28 iwl_mld_set_ctkill(mld, true); 29 30 wiphy_delayed_work_queue(mld->wiphy, &mld->ct_kill_exit_wk, 31 round_jiffies_relative(IWL_MLD_CT_KILL_DURATION)); 32 } 33 34 static void iwl_mld_exit_ctkill(struct wiphy *wiphy, struct wiphy_work *wk) 35 { 36 struct iwl_mld *mld; 37 38 mld = container_of(wk, struct iwl_mld, ct_kill_exit_wk.work); 39 40 IWL_ERR(mld, "Exit CT Kill\n"); 41 iwl_mld_set_ctkill(mld, false); 42 } 43 44 void iwl_mld_handle_temp_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt) 45 { 46 const struct iwl_dts_measurement_notif *notif = 47 (const void *)pkt->data; 48 int temp; 49 u32 ths_crossed; 50 51 temp = le32_to_cpu(notif->temp); 52 53 /* shouldn't be negative, but since it's s32, make sure it isn't */ 54 if (IWL_FW_CHECK(mld, temp < 0, "negative temperature %d\n", temp)) 55 return; 56 57 ths_crossed = le32_to_cpu(notif->threshold_idx); 58 59 /* 0xFF in ths_crossed means the notification is not related 60 * to a trip, so we can ignore it here. 61 */ 62 if (ths_crossed == 0xFF) 63 return; 64 65 IWL_DEBUG_TEMP(mld, "Temp = %d Threshold crossed = %d\n", 66 temp, ths_crossed); 67 68 if (IWL_FW_CHECK(mld, ths_crossed >= IWL_MAX_DTS_TRIPS, 69 "bad threshold: %d\n", ths_crossed)) 70 return; 71 72 #ifdef CONFIG_THERMAL 73 if (mld->tzone) 74 thermal_zone_device_update(mld->tzone, THERMAL_TRIP_VIOLATED); 75 #endif /* CONFIG_THERMAL */ 76 } 77 78 #ifdef CONFIG_THERMAL 79 static int iwl_mld_get_temp(struct iwl_mld *mld, s32 *temp) 80 { 81 struct iwl_host_cmd cmd = { 82 .id = WIDE_ID(PHY_OPS_GROUP, CMD_DTS_MEASUREMENT_TRIGGER_WIDE), 83 .flags = CMD_WANT_SKB, 84 }; 85 const struct iwl_dts_measurement_resp *resp; 86 int ret; 87 88 lockdep_assert_wiphy(mld->wiphy); 89 90 ret = iwl_mld_send_cmd(mld, &cmd); 91 if (ret) { 92 IWL_ERR(mld, 93 "Failed to send the temperature measurement command (err=%d)\n", 94 ret); 95 return ret; 96 } 97 98 if (iwl_rx_packet_payload_len(cmd.resp_pkt) < sizeof(*resp)) { 99 IWL_ERR(mld, 100 "Failed to get a valid response to DTS measurement\n"); 101 ret = -EIO; 102 goto free_resp; 103 } 104 105 resp = (const void *)cmd.resp_pkt->data; 106 *temp = le32_to_cpu(resp->temp); 107 108 IWL_DEBUG_TEMP(mld, 109 "Got temperature measurement response: temp=%d\n", 110 *temp); 111 112 free_resp: 113 iwl_free_resp(&cmd); 114 return ret; 115 } 116 117 static int compare_temps(const void *a, const void *b) 118 { 119 return ((s16)le16_to_cpu(*(__le16 *)a) - 120 (s16)le16_to_cpu(*(__le16 *)b)); 121 } 122 123 struct iwl_trip_walk_data { 124 __le16 *thresholds; 125 int count; 126 }; 127 128 static int iwl_trip_temp_iter(struct thermal_trip *trip, void *arg) 129 { 130 struct iwl_trip_walk_data *twd = arg; 131 132 if (trip->temperature == THERMAL_TEMP_INVALID) 133 return 0; 134 135 twd->thresholds[twd->count++] = 136 cpu_to_le16((s16)(trip->temperature / 1000)); 137 return 0; 138 } 139 #endif 140 141 int iwl_mld_config_temp_report_ths(struct iwl_mld *mld) 142 { 143 struct temp_report_ths_cmd cmd = {0}; 144 int ret; 145 #ifdef CONFIG_THERMAL 146 struct iwl_trip_walk_data twd = { 147 .thresholds = cmd.thresholds, 148 .count = 0 149 }; 150 151 if (!mld->tzone) 152 goto send; 153 154 /* The thermal core holds an array of temperature trips that are 155 * unsorted and uncompressed, the FW should get it compressed and 156 * sorted. 157 */ 158 159 /* compress trips to cmd array, remove uninitialized values*/ 160 for_each_thermal_trip(mld->tzone, iwl_trip_temp_iter, &twd); 161 162 cmd.num_temps = cpu_to_le32(twd.count); 163 if (twd.count) 164 sort(cmd.thresholds, twd.count, sizeof(s16), 165 compare_temps, NULL); 166 167 send: 168 #endif 169 lockdep_assert_wiphy(mld->wiphy); 170 171 ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP, 172 TEMP_REPORTING_THRESHOLDS_CMD), 173 &cmd); 174 if (ret) 175 IWL_ERR(mld, "TEMP_REPORT_THS_CMD command failed (err=%d)\n", 176 ret); 177 178 return ret; 179 } 180 181 #ifdef CONFIG_THERMAL 182 static int iwl_mld_tzone_get_temp(struct thermal_zone_device *device, 183 int *temperature) 184 { 185 struct iwl_mld *mld = thermal_zone_device_priv(device); 186 int temp; 187 int ret = 0; 188 189 wiphy_lock(mld->wiphy); 190 191 if (!mld->fw_status.running) { 192 /* Tell the core that there is no valid temperature value to 193 * return, but it need not worry about this. 194 */ 195 *temperature = THERMAL_TEMP_INVALID; 196 goto unlock; 197 } 198 199 ret = iwl_mld_get_temp(mld, &temp); 200 if (ret) 201 goto unlock; 202 203 *temperature = temp * 1000; 204 unlock: 205 wiphy_unlock(mld->wiphy); 206 return ret; 207 } 208 209 static int iwl_mld_tzone_set_trip_temp(struct thermal_zone_device *device, 210 const struct thermal_trip *trip, 211 int temp) 212 { 213 struct iwl_mld *mld = thermal_zone_device_priv(device); 214 int ret; 215 216 wiphy_lock(mld->wiphy); 217 218 if (!mld->fw_status.running) { 219 ret = -EIO; 220 goto unlock; 221 } 222 223 if ((temp / 1000) > S16_MAX) { 224 ret = -EINVAL; 225 goto unlock; 226 } 227 228 ret = iwl_mld_config_temp_report_ths(mld); 229 unlock: 230 wiphy_unlock(mld->wiphy); 231 return ret; 232 } 233 234 static struct thermal_zone_device_ops tzone_ops = { 235 .get_temp = iwl_mld_tzone_get_temp, 236 .set_trip_temp = iwl_mld_tzone_set_trip_temp, 237 }; 238 239 static void iwl_mld_thermal_zone_register(struct iwl_mld *mld) 240 { 241 int ret; 242 char name[16]; 243 static atomic_t counter = ATOMIC_INIT(0); 244 struct thermal_trip trips[IWL_MAX_DTS_TRIPS] = { 245 [0 ... IWL_MAX_DTS_TRIPS - 1] = { 246 .temperature = THERMAL_TEMP_INVALID, 247 .type = THERMAL_TRIP_PASSIVE, 248 .flags = THERMAL_TRIP_FLAG_RW_TEMP, 249 }, 250 }; 251 252 BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH); 253 254 sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF); 255 mld->tzone = 256 thermal_zone_device_register_with_trips(name, trips, 257 IWL_MAX_DTS_TRIPS, 258 mld, &tzone_ops, 259 NULL, 0, 0); 260 if (IS_ERR(mld->tzone)) { 261 IWL_DEBUG_TEMP(mld, 262 "Failed to register to thermal zone (err = %ld)\n", 263 PTR_ERR(mld->tzone)); 264 mld->tzone = NULL; 265 return; 266 } 267 268 ret = thermal_zone_device_enable(mld->tzone); 269 if (ret) { 270 IWL_DEBUG_TEMP(mld, "Failed to enable thermal zone\n"); 271 thermal_zone_device_unregister(mld->tzone); 272 } 273 } 274 275 /* budget in mWatt */ 276 static const u32 iwl_mld_cdev_budgets[] = { 277 2400, /* cooling state 0 */ 278 2000, /* cooling state 1 */ 279 1800, /* cooling state 2 */ 280 1600, /* cooling state 3 */ 281 1400, /* cooling state 4 */ 282 1200, /* cooling state 5 */ 283 1000, /* cooling state 6 */ 284 900, /* cooling state 7 */ 285 800, /* cooling state 8 */ 286 700, /* cooling state 9 */ 287 650, /* cooling state 10 */ 288 600, /* cooling state 11 */ 289 550, /* cooling state 12 */ 290 500, /* cooling state 13 */ 291 450, /* cooling state 14 */ 292 400, /* cooling state 15 */ 293 350, /* cooling state 16 */ 294 300, /* cooling state 17 */ 295 250, /* cooling state 18 */ 296 200, /* cooling state 19 */ 297 150, /* cooling state 20 */ 298 }; 299 300 int iwl_mld_config_ctdp(struct iwl_mld *mld, u32 state, 301 enum iwl_ctdp_cmd_operation op) 302 { 303 struct iwl_ctdp_cmd cmd = { 304 .operation = cpu_to_le32(op), 305 .budget = cpu_to_le32(iwl_mld_cdev_budgets[state]), 306 .window_size = 0, 307 }; 308 int ret; 309 310 lockdep_assert_wiphy(mld->wiphy); 311 312 ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP, CTDP_CONFIG_CMD), 313 &cmd); 314 315 if (ret) { 316 IWL_ERR(mld, "cTDP command failed (err=%d)\n", ret); 317 return ret; 318 } 319 320 if (op == CTDP_CMD_OPERATION_START) 321 mld->cooling_dev.cur_state = state; 322 323 return 0; 324 } 325 326 static int iwl_mld_tcool_get_max_state(struct thermal_cooling_device *cdev, 327 unsigned long *state) 328 { 329 *state = ARRAY_SIZE(iwl_mld_cdev_budgets) - 1; 330 331 return 0; 332 } 333 334 static int iwl_mld_tcool_get_cur_state(struct thermal_cooling_device *cdev, 335 unsigned long *state) 336 { 337 struct iwl_mld *mld = (struct iwl_mld *)(cdev->devdata); 338 339 *state = mld->cooling_dev.cur_state; 340 341 return 0; 342 } 343 344 static int iwl_mld_tcool_set_cur_state(struct thermal_cooling_device *cdev, 345 unsigned long new_state) 346 { 347 struct iwl_mld *mld = (struct iwl_mld *)(cdev->devdata); 348 int ret; 349 350 wiphy_lock(mld->wiphy); 351 352 if (!mld->fw_status.running) { 353 ret = -EIO; 354 goto unlock; 355 } 356 357 if (new_state >= ARRAY_SIZE(iwl_mld_cdev_budgets)) { 358 ret = -EINVAL; 359 goto unlock; 360 } 361 362 ret = iwl_mld_config_ctdp(mld, new_state, CTDP_CMD_OPERATION_START); 363 364 unlock: 365 wiphy_unlock(mld->wiphy); 366 return ret; 367 } 368 369 static const struct thermal_cooling_device_ops tcooling_ops = { 370 .get_max_state = iwl_mld_tcool_get_max_state, 371 .get_cur_state = iwl_mld_tcool_get_cur_state, 372 .set_cur_state = iwl_mld_tcool_set_cur_state, 373 }; 374 375 static void iwl_mld_cooling_device_register(struct iwl_mld *mld) 376 { 377 char name[] = "iwlwifi"; 378 379 BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH); 380 381 mld->cooling_dev.cdev = 382 thermal_cooling_device_register(name, 383 mld, 384 &tcooling_ops); 385 386 if (IS_ERR(mld->cooling_dev.cdev)) { 387 IWL_DEBUG_TEMP(mld, 388 "Failed to register to cooling device (err = %ld)\n", 389 PTR_ERR(mld->cooling_dev.cdev)); 390 mld->cooling_dev.cdev = NULL; 391 return; 392 } 393 } 394 395 static void iwl_mld_thermal_zone_unregister(struct iwl_mld *mld) 396 { 397 if (!mld->tzone) 398 return; 399 400 IWL_DEBUG_TEMP(mld, "Thermal zone device unregister\n"); 401 if (mld->tzone) { 402 thermal_zone_device_unregister(mld->tzone); 403 mld->tzone = NULL; 404 } 405 } 406 407 static void iwl_mld_cooling_device_unregister(struct iwl_mld *mld) 408 { 409 if (!mld->cooling_dev.cdev) 410 return; 411 412 IWL_DEBUG_TEMP(mld, "Cooling device unregister\n"); 413 if (mld->cooling_dev.cdev) { 414 thermal_cooling_device_unregister(mld->cooling_dev.cdev); 415 mld->cooling_dev.cdev = NULL; 416 } 417 } 418 #endif /* CONFIG_THERMAL */ 419 420 void iwl_mld_thermal_initialize(struct iwl_mld *mld) 421 { 422 wiphy_delayed_work_init(&mld->ct_kill_exit_wk, iwl_mld_exit_ctkill); 423 424 #ifdef CONFIG_THERMAL 425 iwl_mld_cooling_device_register(mld); 426 iwl_mld_thermal_zone_register(mld); 427 #endif 428 } 429 430 void iwl_mld_thermal_exit(struct iwl_mld *mld) 431 { 432 wiphy_delayed_work_cancel(mld->wiphy, &mld->ct_kill_exit_wk); 433 434 #ifdef CONFIG_THERMAL 435 iwl_mld_cooling_device_unregister(mld); 436 iwl_mld_thermal_zone_unregister(mld); 437 #endif 438 } 439