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