FILE
thermal.c
File preview with syntax highlighting.
linux-6.19.14/drivers/net/wireless/intel/iwlwifi/mld/thermal.c
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 21void 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 37static 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 47void 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 82static 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 115free_resp: 116 iwl_free_resp(&cmd); 117 return ret; 118} 119 120static 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 126struct iwl_trip_walk_data { 127 __le16 *thresholds; 128 int count; 129}; 130 131static 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 144int 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 170send: 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 185static 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; 207unlock: 208 wiphy_unlock(mld->wiphy); 209 return ret; 210} 211 212static 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); 232unlock: 233 wiphy_unlock(mld->wiphy); 234 return ret; 235} 236 237static 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 242static 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 278int 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 313static 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 321static 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 331static 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 351unlock: 352 wiphy_unlock(mld->wiphy); 353 return ret; 354} 355 356static 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 362static 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 382static 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 394static 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 407static 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 442void 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 457void 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}
~/
Read-only file preview.