1// SPDX-License-Identifier: ISC 2/* 3 * Copyright (c) 2014-2015 Qualcomm Atheros, Inc. 4 * Copyright (c) 2022 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#include "wmi-ops.h" 15 16static int 17ath10k_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev, 18 unsigned long *state) 19{ 20 *state = ATH10K_THERMAL_THROTTLE_MAX; 21 22 return 0; 23} 24 25static int 26ath10k_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev, 27 unsigned long *state) 28{ 29 struct ath10k *ar = cdev->devdata; 30 31 mutex_lock(&ar->conf_mutex); 32 *state = ar->thermal.throttle_state; 33 mutex_unlock(&ar->conf_mutex); 34 35 return 0; 36} 37 38static int 39ath10k_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev, 40 unsigned long throttle_state) 41{ 42 struct ath10k *ar = cdev->devdata; 43 44 if (throttle_state > ATH10K_THERMAL_THROTTLE_MAX) { 45 ath10k_warn(ar, "throttle state %ld is exceeding the limit %d\n", 46 throttle_state, ATH10K_THERMAL_THROTTLE_MAX); 47 return -EINVAL; 48 } 49 mutex_lock(&ar->conf_mutex); 50 ar->thermal.throttle_state = throttle_state; 51 ath10k_thermal_set_throttling(ar); 52 mutex_unlock(&ar->conf_mutex); 53 return 0; 54} 55 56static const struct thermal_cooling_device_ops ath10k_thermal_ops = { 57 .get_max_state = ath10k_thermal_get_max_throttle_state, 58 .get_cur_state = ath10k_thermal_get_cur_throttle_state, 59 .set_cur_state = ath10k_thermal_set_cur_throttle_state, 60}; 61 62static ssize_t ath10k_thermal_show_temp(struct device *dev, 63 struct device_attribute *attr, 64 char *buf) 65{ 66 struct ath10k *ar = dev_get_drvdata(dev); 67 int ret, temperature; 68 unsigned long time_left; 69 70 mutex_lock(&ar->conf_mutex); 71 72 /* Can't get temperature when the card is off */ 73 if (ar->state != ATH10K_STATE_ON) { 74 ret = -ENETDOWN; 75 goto out; 76 } 77 78 reinit_completion(&ar->thermal.wmi_sync); 79 ret = ath10k_wmi_pdev_get_temperature(ar); 80 if (ret) { 81 ath10k_warn(ar, "failed to read temperature %d\n", ret); 82 goto out; 83 } 84 85 if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) { 86 ret = -ESHUTDOWN; 87 goto out; 88 } 89 90 time_left = wait_for_completion_timeout(&ar->thermal.wmi_sync, 91 ATH10K_THERMAL_SYNC_TIMEOUT_HZ); 92 if (!time_left) { 93 ath10k_warn(ar, "failed to synchronize thermal read\n"); 94 ret = -ETIMEDOUT; 95 goto out; 96 } 97 98 spin_lock_bh(&ar->data_lock); 99 temperature = ar->thermal.temperature; 100 spin_unlock_bh(&ar->data_lock); 101 102 /* display in millidegree celsius */ 103 ret = snprintf(buf, PAGE_SIZE, "%d\n", temperature * 1000); 104out: 105 mutex_unlock(&ar->conf_mutex); 106 return ret; 107} 108 109void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature) 110{ 111 spin_lock_bh(&ar->data_lock); 112 ar->thermal.temperature = temperature; 113 spin_unlock_bh(&ar->data_lock); 114 complete(&ar->thermal.wmi_sync); 115} 116 117static SENSOR_DEVICE_ATTR(temp1_input, 0444, ath10k_thermal_show_temp, 118 NULL, 0); 119 120static struct attribute *ath10k_hwmon_attrs[] = { 121 &sensor_dev_attr_temp1_input.dev_attr.attr, 122 NULL, 123}; 124ATTRIBUTE_GROUPS(ath10k_hwmon); 125 126void ath10k_thermal_set_throttling(struct ath10k *ar) 127{ 128 u32 period, duration, enabled; 129 int ret; 130 131 lockdep_assert_held(&ar->conf_mutex); 132 133 if (!test_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map)) 134 return; 135 136 if (!ar->wmi.ops->gen_pdev_set_quiet_mode) 137 return; 138 139 if (ar->state != ATH10K_STATE_ON) 140 return; 141 142 period = ar->thermal.quiet_period; 143 duration = (period * ar->thermal.throttle_state) / 100; 144 enabled = duration ? 1 : 0; 145 146 ret = ath10k_wmi_pdev_set_quiet_mode(ar, period, duration, 147 ATH10K_QUIET_START_OFFSET, 148 enabled); 149 if (ret) { 150 ath10k_warn(ar, "failed to set quiet mode period %u duarion %u enabled %u ret %d\n", 151 period, duration, enabled, ret); 152 } 153} 154 155int ath10k_thermal_register(struct ath10k *ar) 156{ 157 struct thermal_cooling_device *cdev; 158 struct device *hwmon_dev; 159 int ret; 160 161 if (!test_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map)) 162 return 0; 163 164 cdev = thermal_cooling_device_register("ath10k_thermal", ar, 165 &ath10k_thermal_ops); 166 167 if (IS_ERR(cdev)) { 168 ath10k_err(ar, "failed to setup thermal device result: %ld\n", 169 PTR_ERR(cdev)); 170 return -EINVAL; 171 } 172 173 ret = sysfs_create_link(&ar->dev->kobj, &cdev->device.kobj, 174 "cooling_device"); 175 if (ret) { 176 ath10k_err(ar, "failed to create cooling device symlink\n"); 177 goto err_cooling_destroy; 178 } 179 180 ar->thermal.cdev = cdev; 181 ar->thermal.quiet_period = ATH10K_QUIET_PERIOD_DEFAULT; 182 183 /* Do not register hwmon device when temperature reading is not 184 * supported by firmware 185 */ 186 if (!(ar->wmi.ops->gen_pdev_get_temperature)) 187 return 0; 188 189 /* Avoid linking error on devm_hwmon_device_register_with_groups, I 190 * guess linux/hwmon.h is missing proper stubs. 191 */ 192 if (!IS_REACHABLE(CONFIG_HWMON)) 193 return 0; 194 195 hwmon_dev = devm_hwmon_device_register_with_groups(ar->dev, 196 "ath10k_hwmon", ar, 197 ath10k_hwmon_groups); 198 if (IS_ERR(hwmon_dev)) { 199 ath10k_err(ar, "failed to register hwmon device: %ld\n", 200 PTR_ERR(hwmon_dev)); 201 ret = -EINVAL; 202 goto err_remove_link; 203 } 204 return 0; 205 206err_remove_link: 207 sysfs_remove_link(&ar->dev->kobj, "cooling_device"); 208err_cooling_destroy: 209 thermal_cooling_device_unregister(cdev); 210 return ret; 211} 212 213void ath10k_thermal_unregister(struct ath10k *ar) 214{ 215 if (!test_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map)) 216 return; 217 218 sysfs_remove_link(&ar->dev->kobj, "cooling_device"); 219 thermal_cooling_device_unregister(ar->thermal.cdev); 220} 221