blob: 0b4cd3f16ef8e89357a3f411dd971836b63363e6 [file] [log] [blame]
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +02001/*
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>
Rajkumar Manoharanac2953f2014-12-17 12:22:26 +020020#include <linux/hwmon.h>
21#include <linux/hwmon-sysfs.h>
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +020022#include "core.h"
23#include "debug.h"
24#include "wmi-ops.h"
25
Rajkumar Manoharan972f0512015-03-15 20:36:21 +053026static int
27ath10k_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev,
28 unsigned long *state)
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +020029{
Rajkumar Manoharan972f0512015-03-15 20:36:21 +053030 *state = ATH10K_THERMAL_THROTTLE_MAX;
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +020031
32 return 0;
33}
34
Rajkumar Manoharan972f0512015-03-15 20:36:21 +053035static int
36ath10k_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev,
37 unsigned long *state)
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +020038{
39 struct ath10k *ar = cdev->devdata;
40
41 mutex_lock(&ar->conf_mutex);
Rajkumar Manoharan972f0512015-03-15 20:36:21 +053042 *state = ar->thermal.throttle_state;
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +020043 mutex_unlock(&ar->conf_mutex);
44
45 return 0;
46}
47
Rajkumar Manoharan972f0512015-03-15 20:36:21 +053048static int
49ath10k_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev,
50 unsigned long throttle_state)
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +020051{
52 struct ath10k *ar = cdev->devdata;
Rajkumar Manoharan091105b2015-03-15 20:36:23 +053053 int ret = 0;
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +020054
Rajkumar Manoharan28bf0c42015-03-15 20:36:24 +053055 if (throttle_state > ATH10K_THERMAL_THROTTLE_MAX) {
56 ath10k_warn(ar, "throttle state %ld is exceeding the limit %d\n",
57 throttle_state, ATH10K_THERMAL_THROTTLE_MAX);
58 return -EINVAL;
59 }
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +020060 mutex_lock(&ar->conf_mutex);
Rajkumar Manoharan28bf0c42015-03-15 20:36:24 +053061 ar->thermal.throttle_state = throttle_state;
62
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +020063 if (ar->state != ATH10K_STATE_ON) {
64 ret = -ENETDOWN;
65 goto out;
66 }
67
Rajkumar Manoharan8515b5c2015-03-15 20:36:22 +053068 ath10k_thermal_set_throttling(ar);
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +020069out:
70 mutex_unlock(&ar->conf_mutex);
71 return ret;
72}
73
74static struct thermal_cooling_device_ops ath10k_thermal_ops = {
Rajkumar Manoharan972f0512015-03-15 20:36:21 +053075 .get_max_state = ath10k_thermal_get_max_throttle_state,
76 .get_cur_state = ath10k_thermal_get_cur_throttle_state,
77 .set_cur_state = ath10k_thermal_set_cur_throttle_state,
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +020078};
79
Rajkumar Manoharanac2953f2014-12-17 12:22:26 +020080static ssize_t ath10k_thermal_show_temp(struct device *dev,
81 struct device_attribute *attr,
82 char *buf)
83{
84 struct ath10k *ar = dev_get_drvdata(dev);
85 int ret, temperature;
86
87 mutex_lock(&ar->conf_mutex);
88
89 /* Can't get temperature when the card is off */
90 if (ar->state != ATH10K_STATE_ON) {
91 ret = -ENETDOWN;
92 goto out;
93 }
94
95 reinit_completion(&ar->thermal.wmi_sync);
96 ret = ath10k_wmi_pdev_get_temperature(ar);
97 if (ret) {
98 ath10k_warn(ar, "failed to read temperature %d\n", ret);
99 goto out;
100 }
101
102 if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
103 ret = -ESHUTDOWN;
104 goto out;
105 }
106
107 ret = wait_for_completion_timeout(&ar->thermal.wmi_sync,
108 ATH10K_THERMAL_SYNC_TIMEOUT_HZ);
109 if (ret == 0) {
110 ath10k_warn(ar, "failed to synchronize thermal read\n");
111 ret = -ETIMEDOUT;
112 goto out;
113 }
114
115 spin_lock_bh(&ar->data_lock);
116 temperature = ar->thermal.temperature;
117 spin_unlock_bh(&ar->data_lock);
118
Rajkumar Manoharan6d481612015-01-13 12:44:24 +0530119 /* display in millidegree celcius */
120 ret = snprintf(buf, PAGE_SIZE, "%d\n", temperature * 1000);
Rajkumar Manoharanac2953f2014-12-17 12:22:26 +0200121out:
122 mutex_unlock(&ar->conf_mutex);
123 return ret;
124}
125
126void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature)
127{
128 spin_lock_bh(&ar->data_lock);
129 ar->thermal.temperature = temperature;
130 spin_unlock_bh(&ar->data_lock);
131 complete(&ar->thermal.wmi_sync);
132}
133
134static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ath10k_thermal_show_temp,
135 NULL, 0);
136
137static struct attribute *ath10k_hwmon_attrs[] = {
138 &sensor_dev_attr_temp1_input.dev_attr.attr,
139 NULL,
140};
141ATTRIBUTE_GROUPS(ath10k_hwmon);
142
Rajkumar Manoharan8515b5c2015-03-15 20:36:22 +0530143void ath10k_thermal_set_throttling(struct ath10k *ar)
144{
145 u32 period, duration, enabled;
146 int ret;
147
148 lockdep_assert_held(&ar->conf_mutex);
149
150 period = ar->thermal.quiet_period;
151 duration = (period * ar->thermal.throttle_state) / 100;
152 enabled = duration ? 1 : 0;
153
154 ret = ath10k_wmi_pdev_set_quiet_mode(ar, period, duration,
155 ATH10K_QUIET_START_OFFSET,
156 enabled);
157 if (ret) {
158 ath10k_warn(ar, "failed to set quiet mode period %u duarion %u enabled %u ret %d\n",
159 period, duration, enabled, ret);
160 }
161}
162
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +0200163int ath10k_thermal_register(struct ath10k *ar)
164{
165 struct thermal_cooling_device *cdev;
Rajkumar Manoharanac2953f2014-12-17 12:22:26 +0200166 struct device *hwmon_dev;
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +0200167 int ret;
168
169 cdev = thermal_cooling_device_register("ath10k_thermal", ar,
170 &ath10k_thermal_ops);
171
172 if (IS_ERR(cdev)) {
173 ath10k_err(ar, "failed to setup thermal device result: %ld\n",
174 PTR_ERR(cdev));
175 return -EINVAL;
176 }
177
178 ret = sysfs_create_link(&ar->dev->kobj, &cdev->device.kobj,
179 "cooling_device");
180 if (ret) {
Rajkumar Manoharan7e47e8e2015-03-12 19:32:00 +0200181 ath10k_err(ar, "failed to create cooling device symlink\n");
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +0200182 goto err_cooling_destroy;
183 }
184
185 ar->thermal.cdev = cdev;
Rajkumar Manoharan63fb32d2015-03-15 20:36:20 +0530186 ar->thermal.quiet_period = ATH10K_QUIET_PERIOD_DEFAULT;
Rajkumar Manoharanac2953f2014-12-17 12:22:26 +0200187
188 /* Do not register hwmon device when temperature reading is not
189 * supported by firmware
190 */
191 if (ar->wmi.op_version != ATH10K_FW_WMI_OP_VERSION_10_2_4)
192 return 0;
193
Kalle Valo96bba982015-01-07 17:04:10 +0200194 /* Avoid linking error on devm_hwmon_device_register_with_groups, I
195 * guess linux/hwmon.h is missing proper stubs. */
Rajkumar Manoharana4031af2015-01-13 14:21:45 +0530196 if (!config_enabled(CONFIG_HWMON))
Kalle Valo96bba982015-01-07 17:04:10 +0200197 return 0;
198
Rajkumar Manoharanac2953f2014-12-17 12:22:26 +0200199 hwmon_dev = devm_hwmon_device_register_with_groups(ar->dev,
200 "ath10k_hwmon", ar,
201 ath10k_hwmon_groups);
202 if (IS_ERR(hwmon_dev)) {
203 ath10k_err(ar, "failed to register hwmon device: %ld\n",
204 PTR_ERR(hwmon_dev));
205 ret = -EINVAL;
206 goto err_remove_link;
207 }
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +0200208 return 0;
209
Rajkumar Manoharanac2953f2014-12-17 12:22:26 +0200210err_remove_link:
Rajkumar Manoharan7e47e8e2015-03-12 19:32:00 +0200211 sysfs_remove_link(&ar->dev->kobj, "cooling_device");
Rajkumar Manoharanfe6f36d2014-12-17 12:22:07 +0200212err_cooling_destroy:
213 thermal_cooling_device_unregister(cdev);
214 return ret;
215}
216
217void ath10k_thermal_unregister(struct ath10k *ar)
218{
219 thermal_cooling_device_unregister(ar->thermal.cdev);
220 sysfs_remove_link(&ar->dev->kobj, "cooling_device");
221}