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