18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause-Clear 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2020 The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/device.h> 78c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 88c2ecf20Sopenharmony_ci#include <linux/thermal.h> 98c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 108c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 118c2ecf20Sopenharmony_ci#include "core.h" 128c2ecf20Sopenharmony_ci#include "debug.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic int 158c2ecf20Sopenharmony_ciath11k_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev, 168c2ecf20Sopenharmony_ci unsigned long *state) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci *state = ATH11K_THERMAL_THROTTLE_MAX; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci return 0; 218c2ecf20Sopenharmony_ci} 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic int 248c2ecf20Sopenharmony_ciath11k_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev, 258c2ecf20Sopenharmony_ci unsigned long *state) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct ath11k *ar = cdev->devdata; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci mutex_lock(&ar->conf_mutex); 308c2ecf20Sopenharmony_ci *state = ar->thermal.throttle_state; 318c2ecf20Sopenharmony_ci mutex_unlock(&ar->conf_mutex); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci return 0; 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int 378c2ecf20Sopenharmony_ciath11k_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev, 388c2ecf20Sopenharmony_ci unsigned long throttle_state) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct ath11k *ar = cdev->devdata; 418c2ecf20Sopenharmony_ci int ret; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (throttle_state > ATH11K_THERMAL_THROTTLE_MAX) { 448c2ecf20Sopenharmony_ci ath11k_warn(ar->ab, "throttle state %ld is exceeding the limit %d\n", 458c2ecf20Sopenharmony_ci throttle_state, ATH11K_THERMAL_THROTTLE_MAX); 468c2ecf20Sopenharmony_ci return -EINVAL; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci mutex_lock(&ar->conf_mutex); 498c2ecf20Sopenharmony_ci ret = ath11k_thermal_set_throttling(ar, throttle_state); 508c2ecf20Sopenharmony_ci if (ret == 0) 518c2ecf20Sopenharmony_ci ar->thermal.throttle_state = throttle_state; 528c2ecf20Sopenharmony_ci mutex_unlock(&ar->conf_mutex); 538c2ecf20Sopenharmony_ci return ret; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic const struct thermal_cooling_device_ops ath11k_thermal_ops = { 578c2ecf20Sopenharmony_ci .get_max_state = ath11k_thermal_get_max_throttle_state, 588c2ecf20Sopenharmony_ci .get_cur_state = ath11k_thermal_get_cur_throttle_state, 598c2ecf20Sopenharmony_ci .set_cur_state = ath11k_thermal_set_cur_throttle_state, 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic ssize_t ath11k_thermal_show_temp(struct device *dev, 638c2ecf20Sopenharmony_ci struct device_attribute *attr, 648c2ecf20Sopenharmony_ci char *buf) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct ath11k *ar = dev_get_drvdata(dev); 678c2ecf20Sopenharmony_ci int ret, temperature; 688c2ecf20Sopenharmony_ci unsigned long time_left; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci mutex_lock(&ar->conf_mutex); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* Can't get temperature when the card is off */ 738c2ecf20Sopenharmony_ci if (ar->state != ATH11K_STATE_ON) { 748c2ecf20Sopenharmony_ci ret = -ENETDOWN; 758c2ecf20Sopenharmony_ci goto out; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci reinit_completion(&ar->thermal.wmi_sync); 798c2ecf20Sopenharmony_ci ret = ath11k_wmi_send_pdev_temperature_cmd(ar); 808c2ecf20Sopenharmony_ci if (ret) { 818c2ecf20Sopenharmony_ci ath11k_warn(ar->ab, "failed to read temperature %d\n", ret); 828c2ecf20Sopenharmony_ci goto out; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) { 868c2ecf20Sopenharmony_ci ret = -ESHUTDOWN; 878c2ecf20Sopenharmony_ci goto out; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci time_left = wait_for_completion_timeout(&ar->thermal.wmi_sync, 918c2ecf20Sopenharmony_ci ATH11K_THERMAL_SYNC_TIMEOUT_HZ); 928c2ecf20Sopenharmony_ci if (!time_left) { 938c2ecf20Sopenharmony_ci ath11k_warn(ar->ab, "failed to synchronize thermal read\n"); 948c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 958c2ecf20Sopenharmony_ci goto out; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci spin_lock_bh(&ar->data_lock); 998c2ecf20Sopenharmony_ci temperature = ar->thermal.temperature; 1008c2ecf20Sopenharmony_ci spin_unlock_bh(&ar->data_lock); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* display in millidegree celcius */ 1038c2ecf20Sopenharmony_ci ret = snprintf(buf, PAGE_SIZE, "%d\n", temperature * 1000); 1048c2ecf20Sopenharmony_ciout: 1058c2ecf20Sopenharmony_ci mutex_unlock(&ar->conf_mutex); 1068c2ecf20Sopenharmony_ci return ret; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_civoid ath11k_thermal_event_temperature(struct ath11k *ar, int temperature) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci spin_lock_bh(&ar->data_lock); 1128c2ecf20Sopenharmony_ci ar->thermal.temperature = temperature; 1138c2ecf20Sopenharmony_ci spin_unlock_bh(&ar->data_lock); 1148c2ecf20Sopenharmony_ci complete(&ar->thermal.wmi_sync); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_input, 0444, ath11k_thermal_show_temp, 1188c2ecf20Sopenharmony_ci NULL, 0); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic struct attribute *ath11k_hwmon_attrs[] = { 1218c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, 1228c2ecf20Sopenharmony_ci NULL, 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(ath11k_hwmon); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ciint ath11k_thermal_set_throttling(struct ath11k *ar, u32 throttle_state) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct ath11k_base *sc = ar->ab; 1298c2ecf20Sopenharmony_ci struct thermal_mitigation_params param; 1308c2ecf20Sopenharmony_ci int ret = 0; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci lockdep_assert_held(&ar->conf_mutex); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (ar->state != ATH11K_STATE_ON) 1358c2ecf20Sopenharmony_ci return 0; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci memset(¶m, 0, sizeof(param)); 1388c2ecf20Sopenharmony_ci param.pdev_id = ar->pdev->pdev_id; 1398c2ecf20Sopenharmony_ci param.enable = throttle_state ? 1 : 0; 1408c2ecf20Sopenharmony_ci param.dc = ATH11K_THERMAL_DEFAULT_DUTY_CYCLE; 1418c2ecf20Sopenharmony_ci param.dc_per_event = 0xFFFFFFFF; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci param.levelconf[0].tmplwm = ATH11K_THERMAL_TEMP_LOW_MARK; 1448c2ecf20Sopenharmony_ci param.levelconf[0].tmphwm = ATH11K_THERMAL_TEMP_HIGH_MARK; 1458c2ecf20Sopenharmony_ci param.levelconf[0].dcoffpercent = throttle_state; 1468c2ecf20Sopenharmony_ci param.levelconf[0].priority = 0; /* disable all data tx queues */ 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci ret = ath11k_wmi_send_thermal_mitigation_param_cmd(ar, ¶m); 1498c2ecf20Sopenharmony_ci if (ret) { 1508c2ecf20Sopenharmony_ci ath11k_warn(sc, "failed to send thermal mitigation duty cycle %u ret %d\n", 1518c2ecf20Sopenharmony_ci throttle_state, ret); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return ret; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ciint ath11k_thermal_register(struct ath11k_base *sc) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct thermal_cooling_device *cdev; 1608c2ecf20Sopenharmony_ci struct device *hwmon_dev; 1618c2ecf20Sopenharmony_ci struct ath11k *ar; 1628c2ecf20Sopenharmony_ci struct ath11k_pdev *pdev; 1638c2ecf20Sopenharmony_ci int i, ret; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci for (i = 0; i < sc->num_radios; i++) { 1668c2ecf20Sopenharmony_ci pdev = &sc->pdevs[i]; 1678c2ecf20Sopenharmony_ci ar = pdev->ar; 1688c2ecf20Sopenharmony_ci if (!ar) 1698c2ecf20Sopenharmony_ci continue; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci cdev = thermal_cooling_device_register("ath11k_thermal", ar, 1728c2ecf20Sopenharmony_ci &ath11k_thermal_ops); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (IS_ERR(cdev)) { 1758c2ecf20Sopenharmony_ci ath11k_err(sc, "failed to setup thermal device result: %ld\n", 1768c2ecf20Sopenharmony_ci PTR_ERR(cdev)); 1778c2ecf20Sopenharmony_ci ret = -EINVAL; 1788c2ecf20Sopenharmony_ci goto err_thermal_destroy; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci ar->thermal.cdev = cdev; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci ret = sysfs_create_link(&ar->hw->wiphy->dev.kobj, &cdev->device.kobj, 1848c2ecf20Sopenharmony_ci "cooling_device"); 1858c2ecf20Sopenharmony_ci if (ret) { 1868c2ecf20Sopenharmony_ci ath11k_err(sc, "failed to create cooling device symlink\n"); 1878c2ecf20Sopenharmony_ci goto err_thermal_destroy; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (!IS_REACHABLE(CONFIG_HWMON)) 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_groups(&ar->hw->wiphy->dev, 1948c2ecf20Sopenharmony_ci "ath11k_hwmon", ar, 1958c2ecf20Sopenharmony_ci ath11k_hwmon_groups); 1968c2ecf20Sopenharmony_ci if (IS_ERR(hwmon_dev)) { 1978c2ecf20Sopenharmony_ci ath11k_err(ar->ab, "failed to register hwmon device: %ld\n", 1988c2ecf20Sopenharmony_ci PTR_ERR(hwmon_dev)); 1998c2ecf20Sopenharmony_ci ret = -EINVAL; 2008c2ecf20Sopenharmony_ci goto err_thermal_destroy; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cierr_thermal_destroy: 2078c2ecf20Sopenharmony_ci ath11k_thermal_unregister(sc); 2088c2ecf20Sopenharmony_ci return ret; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_civoid ath11k_thermal_unregister(struct ath11k_base *sc) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct ath11k *ar; 2148c2ecf20Sopenharmony_ci struct ath11k_pdev *pdev; 2158c2ecf20Sopenharmony_ci int i; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci for (i = 0; i < sc->num_radios; i++) { 2188c2ecf20Sopenharmony_ci pdev = &sc->pdevs[i]; 2198c2ecf20Sopenharmony_ci ar = pdev->ar; 2208c2ecf20Sopenharmony_ci if (!ar) 2218c2ecf20Sopenharmony_ci continue; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci sysfs_remove_link(&ar->hw->wiphy->dev.kobj, "cooling_device"); 2248c2ecf20Sopenharmony_ci thermal_cooling_device_unregister(ar->thermal.cdev); 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci} 227