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