1 // SPDX-License-Identifier: BSD-3-Clause-Clear 2 /* 3 * Copyright (c) 2020 The Linux Foundation. All rights reserved. 4 * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. 5 */ 6 7 #include <linux/device.h> 8 #include <linux/sysfs.h> 9 #include <linux/thermal.h> 10 #include <linux/hwmon.h> 11 #include <linux/hwmon-sysfs.h> 12 #include "core.h" 13 #include "debug.h" 14 15 static int 16 ath11k_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev, 17 unsigned long *state) 18 { 19 *state = ATH11K_THERMAL_THROTTLE_MAX; 20 21 return 0; 22 } 23 24 static int 25 ath11k_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev, 26 unsigned long *state) 27 { 28 struct ath11k *ar = cdev->devdata; 29 30 mutex_lock(&ar->conf_mutex); 31 *state = ar->thermal.throttle_state; 32 mutex_unlock(&ar->conf_mutex); 33 34 return 0; 35 } 36 37 static int 38 ath11k_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev, 39 unsigned long throttle_state) 40 { 41 struct ath11k *ar = cdev->devdata; 42 int ret; 43 44 if (throttle_state > ATH11K_THERMAL_THROTTLE_MAX) { 45 ath11k_warn(ar->ab, "throttle state %ld is exceeding the limit %d\n", 46 throttle_state, ATH11K_THERMAL_THROTTLE_MAX); 47 return -EINVAL; 48 } 49 mutex_lock(&ar->conf_mutex); 50 ret = ath11k_thermal_set_throttling(ar, throttle_state); 51 if (ret == 0) 52 ar->thermal.throttle_state = throttle_state; 53 mutex_unlock(&ar->conf_mutex); 54 return ret; 55 } 56 57 static const struct thermal_cooling_device_ops ath11k_thermal_ops = { 58 .get_max_state = ath11k_thermal_get_max_throttle_state, 59 .get_cur_state = ath11k_thermal_get_cur_throttle_state, 60 .set_cur_state = ath11k_thermal_set_cur_throttle_state, 61 }; 62 63 static ssize_t ath11k_thermal_show_temp(struct device *dev, 64 struct device_attribute *attr, 65 char *buf) 66 { 67 struct ath11k *ar = dev_get_drvdata(dev); 68 int ret, temperature; 69 unsigned long time_left; 70 71 mutex_lock(&ar->conf_mutex); 72 73 /* Can't get temperature when the card is off */ 74 if (ar->state != ATH11K_STATE_ON) { 75 ret = -ENETDOWN; 76 goto out; 77 } 78 79 reinit_completion(&ar->thermal.wmi_sync); 80 ret = ath11k_wmi_send_pdev_temperature_cmd(ar); 81 if (ret) { 82 ath11k_warn(ar->ab, "failed to read temperature %d\n", ret); 83 goto out; 84 } 85 86 if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) { 87 ret = -ESHUTDOWN; 88 goto out; 89 } 90 91 time_left = wait_for_completion_timeout(&ar->thermal.wmi_sync, 92 ATH11K_THERMAL_SYNC_TIMEOUT_HZ); 93 if (!time_left) { 94 ath11k_warn(ar->ab, "failed to synchronize thermal read\n"); 95 ret = -ETIMEDOUT; 96 goto out; 97 } 98 99 spin_lock_bh(&ar->data_lock); 100 temperature = ar->thermal.temperature; 101 spin_unlock_bh(&ar->data_lock); 102 103 /* display in millidegree Celsius */ 104 ret = sysfs_emit(buf, "%d\n", temperature * 1000); 105 out: 106 mutex_unlock(&ar->conf_mutex); 107 return ret; 108 } 109 110 void ath11k_thermal_event_temperature(struct ath11k *ar, int temperature) 111 { 112 spin_lock_bh(&ar->data_lock); 113 ar->thermal.temperature = temperature; 114 spin_unlock_bh(&ar->data_lock); 115 complete(&ar->thermal.wmi_sync); 116 } 117 118 static SENSOR_DEVICE_ATTR(temp1_input, 0444, ath11k_thermal_show_temp, 119 NULL, 0); 120 121 static struct attribute *ath11k_hwmon_attrs[] = { 122 &sensor_dev_attr_temp1_input.dev_attr.attr, 123 NULL, 124 }; 125 ATTRIBUTE_GROUPS(ath11k_hwmon); 126 127 int ath11k_thermal_set_throttling(struct ath11k *ar, u32 throttle_state) 128 { 129 struct ath11k_base *ab = ar->ab; 130 struct thermal_mitigation_params param; 131 int ret = 0; 132 133 lockdep_assert_held(&ar->conf_mutex); 134 135 if (ar->state != ATH11K_STATE_ON) 136 return 0; 137 138 memset(¶m, 0, sizeof(param)); 139 param.pdev_id = ar->pdev->pdev_id; 140 param.enable = throttle_state ? 1 : 0; 141 param.dc = ATH11K_THERMAL_DEFAULT_DUTY_CYCLE; 142 param.dc_per_event = 0xFFFFFFFF; 143 144 param.levelconf[0].tmplwm = ATH11K_THERMAL_TEMP_LOW_MARK; 145 param.levelconf[0].tmphwm = ATH11K_THERMAL_TEMP_HIGH_MARK; 146 param.levelconf[0].dcoffpercent = throttle_state; 147 param.levelconf[0].priority = 0; /* disable all data tx queues */ 148 149 ret = ath11k_wmi_send_thermal_mitigation_param_cmd(ar, ¶m); 150 if (ret) { 151 ath11k_warn(ab, "failed to send thermal mitigation duty cycle %u ret %d\n", 152 throttle_state, ret); 153 } 154 155 return ret; 156 } 157 158 int ath11k_thermal_register(struct ath11k_base *ab) 159 { 160 struct thermal_cooling_device *cdev; 161 struct device *hwmon_dev; 162 struct ath11k *ar; 163 struct ath11k_pdev *pdev; 164 int i, ret; 165 166 if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) 167 return 0; 168 169 for (i = 0; i < ab->num_radios; i++) { 170 pdev = &ab->pdevs[i]; 171 ar = pdev->ar; 172 if (!ar) 173 continue; 174 175 cdev = thermal_cooling_device_register("ath11k_thermal", ar, 176 &ath11k_thermal_ops); 177 178 if (IS_ERR(cdev)) { 179 ath11k_err(ab, "failed to setup thermal device result: %ld\n", 180 PTR_ERR(cdev)); 181 ret = -EINVAL; 182 goto err_thermal_destroy; 183 } 184 185 ar->thermal.cdev = cdev; 186 187 ret = sysfs_create_link(&ar->hw->wiphy->dev.kobj, &cdev->device.kobj, 188 "cooling_device"); 189 if (ret) { 190 ath11k_err(ab, "failed to create cooling device symlink\n"); 191 goto err_thermal_destroy; 192 } 193 194 if (!IS_REACHABLE(CONFIG_HWMON)) 195 return 0; 196 197 hwmon_dev = devm_hwmon_device_register_with_groups(&ar->hw->wiphy->dev, 198 "ath11k_hwmon", ar, 199 ath11k_hwmon_groups); 200 if (IS_ERR(hwmon_dev)) { 201 ath11k_err(ar->ab, "failed to register hwmon device: %ld\n", 202 PTR_ERR(hwmon_dev)); 203 ret = -EINVAL; 204 goto err_thermal_destroy; 205 } 206 } 207 208 return 0; 209 210 err_thermal_destroy: 211 ath11k_thermal_unregister(ab); 212 return ret; 213 } 214 215 void ath11k_thermal_unregister(struct ath11k_base *ab) 216 { 217 struct ath11k *ar; 218 struct ath11k_pdev *pdev; 219 int i; 220 221 for (i = 0; i < ab->num_radios; i++) { 222 pdev = &ab->pdevs[i]; 223 ar = pdev->ar; 224 if (!ar) 225 continue; 226 227 sysfs_remove_link(&ar->hw->wiphy->dev.kobj, "cooling_device"); 228 thermal_cooling_device_unregister(ar->thermal.cdev); 229 } 230 } 231