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