Add thermal cooling device support to control the temperature by throttling data transmission. Throttling is performed by suspending data TX queues according to a configured duty-cycle off percentage. The thermal cooling device allows users to configure the duty-cycle off percentage and operate the device with the selected value. User configuration updates a single duty-cycle off percentage, which is applied uniformly by the host and treated as only one temperature level. This value remains in effect until updated again by the user. All other thermal throttling parameters continue to use their default firmware provided values. Reject invalid duty-cycle off percentage values that fall outside the supported range. Register a cooling device to allow the thermal framework to query and set the current throttle state, report the maximum supported state, and keep the host state in sync with successful firmware updates. A throttle state of zero restores the default firmware thermal configuration. Command to set the duty-cycle off percent: echo 40 > /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device0/cur_state Command to read duty-cycle off percent: cat /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device0/cur_state Command to read the maximum duty-cycle off percent: cat /sys/devices/pci0000:00/0000:00:1d.1/0000:58:00.0/ieee80211/phyX/cooling_device0/max_state Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Maharaja Kennadyrajan --- drivers/net/wireless/ath/ath12k/mac.c | 1 + drivers/net/wireless/ath/ath12k/thermal.c | 107 +++++++++++++++++++++- drivers/net/wireless/ath/ath12k/thermal.h | 14 +++ 3 files changed, 121 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 21430a70aa7c..40395c9a8bea 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -14821,6 +14821,7 @@ static void ath12k_mac_setup(struct ath12k *ar) init_completion(&ar->completed_11d_scan); init_completion(&ar->regd_update_completed); init_completion(&ar->thermal.wmi_sync); + mutex_init(&ar->thermal.lock); ar->thermal.temperature = 0; ar->thermal.hwmon_dev = NULL; diff --git a/drivers/net/wireless/ath/ath12k/thermal.c b/drivers/net/wireless/ath/ath12k/thermal.c index 6f70c11c1098..97fc49c40ac1 100644 --- a/drivers/net/wireless/ath/ath12k/thermal.c +++ b/drivers/net/wireless/ath/ath12k/thermal.c @@ -76,6 +76,73 @@ void ath12k_thermal_init_configs(struct ath12k *ar) ar->thermal.tt_level_configs = &tt_level_configs[cfg_idx][0]; } +static int +ath12k_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = ATH12K_THERMAL_THROTTLE_MAX; + + return 0; +} + +static int +ath12k_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct ath12k *ar = cdev->devdata; + + mutex_lock(&ar->thermal.lock); + *state = ar->thermal.throttle_state; + mutex_unlock(&ar->thermal.lock); + + return 0; +} + +int ath12k_thermal_set_throttling(struct ath12k *ar, u32 throttle_state) +{ + struct ath12k_wmi_thermal_mitigation_arg param = {}; + struct ath12k_wmi_tt_level_config_param cfg = {}; + int ret; + + param.num_levels = 1; + cfg.dcoffpercent = throttle_state; + param.levelconf = &cfg; + + ret = ath12k_wmi_send_thermal_mitigation_cmd(ar, ¶m); + if (ret) + ath12k_warn(ar->ab, "failed to send thermal mitigation cmd: %d\n", + ret); + + return ret; +} + +static int +ath12k_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev, + unsigned long throttle_state) +{ + struct ath12k *ar = cdev->devdata; + + if (throttle_state > ATH12K_THERMAL_THROTTLE_MAX) + return -EINVAL; + + scoped_guard(mutex, &ar->thermal.lock) { + if (ar->thermal.throttle_state == throttle_state) + return 0; + ar->thermal.throttle_state = throttle_state; + } + + if (throttle_state == 0) + return ath12k_thermal_throttling_config_default(ar); + + return ath12k_thermal_set_throttling(ar, throttle_state); +} + +static const struct thermal_cooling_device_ops ath12k_thermal_ops = { + .get_max_state = ath12k_thermal_get_max_throttle_state, + .get_cur_state = ath12k_thermal_get_cur_throttle_state, + .set_cur_state = ath12k_thermal_set_cur_throttle_state, +}; + static ssize_t ath12k_thermal_temp_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -132,6 +199,7 @@ ATTRIBUTE_GROUPS(ath12k_hwmon); static int ath12k_thermal_setup_radio(struct ath12k_base *ab, int i) { + char pdev_name[20]; struct ath12k *ar; int ret; @@ -139,6 +207,28 @@ static int ath12k_thermal_setup_radio(struct ath12k_base *ab, int i) if (!ar) return 0; + ar->thermal.cdev = + thermal_cooling_device_register("ath12k_thermal", ar, + &ath12k_thermal_ops); + if (IS_ERR(ar->thermal.cdev)) { + ret = PTR_ERR(ar->thermal.cdev); + ar->thermal.cdev = NULL; + ath12k_err(ar->ab, "failed to register cooling device: %d\n", + ret); + return ret; + } + + scnprintf(pdev_name, sizeof(pdev_name), "cooling_device%u", + ar->hw_link_id); + + ret = sysfs_create_link(&ar->ah->hw->wiphy->dev.kobj, + &ar->thermal.cdev->device.kobj, pdev_name); + if (ret) { + ath12k_err(ab, "failed to create cooling device symlink: %d\n", + ret); + goto unregister_cdev; + } + ar->thermal.hwmon_dev = hwmon_device_register_with_groups(&ar->ah->hw->wiphy->dev, "ath12k_hwmon", ar, @@ -148,14 +238,22 @@ static int ath12k_thermal_setup_radio(struct ath12k_base *ab, int i) ar->thermal.hwmon_dev = NULL; ath12k_err(ar->ab, "failed to register hwmon device: %d\n", ret); - return ret; + goto remove_sysfs; } return 0; + +remove_sysfs: + sysfs_remove_link(&ar->ah->hw->wiphy->dev.kobj, pdev_name); +unregister_cdev: + thermal_cooling_device_unregister(ar->thermal.cdev); + ar->thermal.cdev = NULL; + return ret; } static void ath12k_thermal_cleanup_radio(struct ath12k_base *ab, int i) { + char pdev_name[20]; struct ath12k *ar; ar = ab->pdevs[i].ar; @@ -164,6 +262,13 @@ static void ath12k_thermal_cleanup_radio(struct ath12k_base *ab, int i) hwmon_device_unregister(ar->thermal.hwmon_dev); ar->thermal.hwmon_dev = NULL; + + scnprintf(pdev_name, sizeof(pdev_name), "cooling_device%u", + ar->hw_link_id); + sysfs_remove_link(&ar->ah->hw->wiphy->dev.kobj, pdev_name); + + thermal_cooling_device_unregister(ar->thermal.cdev); + ar->thermal.cdev = NULL; } int ath12k_thermal_register(struct ath12k_base *ab) diff --git a/drivers/net/wireless/ath/ath12k/thermal.h b/drivers/net/wireless/ath/ath12k/thermal.h index 33231bb3683c..30e7b0880e05 100644 --- a/drivers/net/wireless/ath/ath12k/thermal.h +++ b/drivers/net/wireless/ath/ath12k/thermal.h @@ -7,9 +7,12 @@ #ifndef _ATH12K_THERMAL_ #define _ATH12K_THERMAL_ +#include + #define ATH12K_THERMAL_SYNC_TIMEOUT_HZ (5 * HZ) #define ATH12K_THERMAL_DEFAULT_DUTY_CYCLE 100 +#define ATH12K_THERMAL_THROTTLE_MAX 100 enum ath12k_thermal_cfg_idx { /* Internal Power Amplifier Device */ @@ -26,6 +29,10 @@ struct ath12k_thermal { int temperature; struct device *hwmon_dev; const struct ath12k_wmi_tt_level_config_param *tt_level_configs; + struct thermal_cooling_device *cdev; + /* Serialize thermal operations and hwmon reads */ + struct mutex lock; + u32 throttle_state; }; #if IS_REACHABLE(CONFIG_THERMAL) @@ -34,6 +41,7 @@ void ath12k_thermal_unregister(struct ath12k_base *ab); void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature); int ath12k_thermal_throttling_config_default(struct ath12k *ar); void ath12k_thermal_init_configs(struct ath12k *ar); +int ath12k_thermal_set_throttling(struct ath12k *ar, u32 throttle_state); #else static inline int ath12k_thermal_register(struct ath12k_base *ab) { @@ -57,5 +65,11 @@ static inline int ath12k_thermal_throttling_config_default(struct ath12k *ar) static inline void ath12k_thermal_init_configs(struct ath12k *ar) { } + +static inline int ath12k_thermal_set_throttling(struct ath12k *ar, + u32 throttle_state) +{ + return 0; +} #endif #endif /* _ATH12K_THERMAL_ */ -- 2.34.1