1f0553ca9SKalle Valo // SPDX-License-Identifier: ISC
2fe6f36d6SRajkumar Manoharan /*
38b1083d6SKalle Valo * Copyright (c) 2014-2015 Qualcomm Atheros, Inc.
4*358c36eaSJeff Johnson * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
5fe6f36d6SRajkumar Manoharan */
6fe6f36d6SRajkumar Manoharan
7fe6f36d6SRajkumar Manoharan #include <linux/device.h>
8fe6f36d6SRajkumar Manoharan #include <linux/sysfs.h>
9fe6f36d6SRajkumar Manoharan #include <linux/thermal.h>
10ac2953fcSRajkumar Manoharan #include <linux/hwmon.h>
11ac2953fcSRajkumar Manoharan #include <linux/hwmon-sysfs.h>
12fe6f36d6SRajkumar Manoharan #include "core.h"
13fe6f36d6SRajkumar Manoharan #include "debug.h"
14fe6f36d6SRajkumar Manoharan #include "wmi-ops.h"
15fe6f36d6SRajkumar Manoharan
16972f0513SRajkumar Manoharan static int
ath10k_thermal_get_max_throttle_state(struct thermal_cooling_device * cdev,unsigned long * state)17972f0513SRajkumar Manoharan ath10k_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev,
18fe6f36d6SRajkumar Manoharan unsigned long *state)
19fe6f36d6SRajkumar Manoharan {
20972f0513SRajkumar Manoharan *state = ATH10K_THERMAL_THROTTLE_MAX;
21fe6f36d6SRajkumar Manoharan
22fe6f36d6SRajkumar Manoharan return 0;
23fe6f36d6SRajkumar Manoharan }
24fe6f36d6SRajkumar Manoharan
25972f0513SRajkumar Manoharan static int
ath10k_thermal_get_cur_throttle_state(struct thermal_cooling_device * cdev,unsigned long * state)26972f0513SRajkumar Manoharan ath10k_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev,
27fe6f36d6SRajkumar Manoharan unsigned long *state)
28fe6f36d6SRajkumar Manoharan {
29fe6f36d6SRajkumar Manoharan struct ath10k *ar = cdev->devdata;
30fe6f36d6SRajkumar Manoharan
31fe6f36d6SRajkumar Manoharan mutex_lock(&ar->conf_mutex);
32972f0513SRajkumar Manoharan *state = ar->thermal.throttle_state;
33fe6f36d6SRajkumar Manoharan mutex_unlock(&ar->conf_mutex);
34fe6f36d6SRajkumar Manoharan
35fe6f36d6SRajkumar Manoharan return 0;
36fe6f36d6SRajkumar Manoharan }
37fe6f36d6SRajkumar Manoharan
38972f0513SRajkumar Manoharan static int
ath10k_thermal_set_cur_throttle_state(struct thermal_cooling_device * cdev,unsigned long throttle_state)39972f0513SRajkumar Manoharan ath10k_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev,
40972f0513SRajkumar Manoharan unsigned long throttle_state)
41fe6f36d6SRajkumar Manoharan {
42fe6f36d6SRajkumar Manoharan struct ath10k *ar = cdev->devdata;
43fe6f36d6SRajkumar Manoharan
4428bf0c4eSRajkumar Manoharan if (throttle_state > ATH10K_THERMAL_THROTTLE_MAX) {
4528bf0c4eSRajkumar Manoharan ath10k_warn(ar, "throttle state %ld is exceeding the limit %d\n",
4628bf0c4eSRajkumar Manoharan throttle_state, ATH10K_THERMAL_THROTTLE_MAX);
4728bf0c4eSRajkumar Manoharan return -EINVAL;
4828bf0c4eSRajkumar Manoharan }
49fe6f36d6SRajkumar Manoharan mutex_lock(&ar->conf_mutex);
5028bf0c4eSRajkumar Manoharan ar->thermal.throttle_state = throttle_state;
518515b5c7SRajkumar Manoharan ath10k_thermal_set_throttling(ar);
52fe6f36d6SRajkumar Manoharan mutex_unlock(&ar->conf_mutex);
539936fa59SRajkumar Manoharan return 0;
54fe6f36d6SRajkumar Manoharan }
55fe6f36d6SRajkumar Manoharan
561cdb6c9fSBhumika Goyal static const struct thermal_cooling_device_ops ath10k_thermal_ops = {
57972f0513SRajkumar Manoharan .get_max_state = ath10k_thermal_get_max_throttle_state,
58972f0513SRajkumar Manoharan .get_cur_state = ath10k_thermal_get_cur_throttle_state,
59972f0513SRajkumar Manoharan .set_cur_state = ath10k_thermal_set_cur_throttle_state,
60fe6f36d6SRajkumar Manoharan };
61fe6f36d6SRajkumar Manoharan
ath10k_thermal_show_temp(struct device * dev,struct device_attribute * attr,char * buf)62ac2953fcSRajkumar Manoharan static ssize_t ath10k_thermal_show_temp(struct device *dev,
63ac2953fcSRajkumar Manoharan struct device_attribute *attr,
64ac2953fcSRajkumar Manoharan char *buf)
65ac2953fcSRajkumar Manoharan {
66ac2953fcSRajkumar Manoharan struct ath10k *ar = dev_get_drvdata(dev);
67ac2953fcSRajkumar Manoharan int ret, temperature;
68f439539bSNicholas Mc Guire unsigned long time_left;
69ac2953fcSRajkumar Manoharan
70ac2953fcSRajkumar Manoharan mutex_lock(&ar->conf_mutex);
71ac2953fcSRajkumar Manoharan
72ac2953fcSRajkumar Manoharan /* Can't get temperature when the card is off */
73ac2953fcSRajkumar Manoharan if (ar->state != ATH10K_STATE_ON) {
74ac2953fcSRajkumar Manoharan ret = -ENETDOWN;
75ac2953fcSRajkumar Manoharan goto out;
76ac2953fcSRajkumar Manoharan }
77ac2953fcSRajkumar Manoharan
78ac2953fcSRajkumar Manoharan reinit_completion(&ar->thermal.wmi_sync);
79ac2953fcSRajkumar Manoharan ret = ath10k_wmi_pdev_get_temperature(ar);
80ac2953fcSRajkumar Manoharan if (ret) {
81ac2953fcSRajkumar Manoharan ath10k_warn(ar, "failed to read temperature %d\n", ret);
82ac2953fcSRajkumar Manoharan goto out;
83ac2953fcSRajkumar Manoharan }
84ac2953fcSRajkumar Manoharan
85ac2953fcSRajkumar Manoharan if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
86ac2953fcSRajkumar Manoharan ret = -ESHUTDOWN;
87ac2953fcSRajkumar Manoharan goto out;
88ac2953fcSRajkumar Manoharan }
89ac2953fcSRajkumar Manoharan
90f439539bSNicholas Mc Guire time_left = wait_for_completion_timeout(&ar->thermal.wmi_sync,
91ac2953fcSRajkumar Manoharan ATH10K_THERMAL_SYNC_TIMEOUT_HZ);
92f439539bSNicholas Mc Guire if (!time_left) {
93ac2953fcSRajkumar Manoharan ath10k_warn(ar, "failed to synchronize thermal read\n");
94ac2953fcSRajkumar Manoharan ret = -ETIMEDOUT;
95ac2953fcSRajkumar Manoharan goto out;
96ac2953fcSRajkumar Manoharan }
97ac2953fcSRajkumar Manoharan
98ac2953fcSRajkumar Manoharan spin_lock_bh(&ar->data_lock);
99ac2953fcSRajkumar Manoharan temperature = ar->thermal.temperature;
100ac2953fcSRajkumar Manoharan spin_unlock_bh(&ar->data_lock);
101ac2953fcSRajkumar Manoharan
102b8a71b95SJeff Johnson /* display in millidegree celsius */
1036d481616SRajkumar Manoharan ret = snprintf(buf, PAGE_SIZE, "%d\n", temperature * 1000);
104ac2953fcSRajkumar Manoharan out:
105ac2953fcSRajkumar Manoharan mutex_unlock(&ar->conf_mutex);
106ac2953fcSRajkumar Manoharan return ret;
107ac2953fcSRajkumar Manoharan }
108ac2953fcSRajkumar Manoharan
ath10k_thermal_event_temperature(struct ath10k * ar,int temperature)109ac2953fcSRajkumar Manoharan void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature)
110ac2953fcSRajkumar Manoharan {
111ac2953fcSRajkumar Manoharan spin_lock_bh(&ar->data_lock);
112ac2953fcSRajkumar Manoharan ar->thermal.temperature = temperature;
113ac2953fcSRajkumar Manoharan spin_unlock_bh(&ar->data_lock);
114ac2953fcSRajkumar Manoharan complete(&ar->thermal.wmi_sync);
115ac2953fcSRajkumar Manoharan }
116ac2953fcSRajkumar Manoharan
11753c8d48bSMarcin Rokicki static SENSOR_DEVICE_ATTR(temp1_input, 0444, ath10k_thermal_show_temp,
118ac2953fcSRajkumar Manoharan NULL, 0);
119ac2953fcSRajkumar Manoharan
120ac2953fcSRajkumar Manoharan static struct attribute *ath10k_hwmon_attrs[] = {
121ac2953fcSRajkumar Manoharan &sensor_dev_attr_temp1_input.dev_attr.attr,
122ac2953fcSRajkumar Manoharan NULL,
123ac2953fcSRajkumar Manoharan };
124ac2953fcSRajkumar Manoharan ATTRIBUTE_GROUPS(ath10k_hwmon);
125ac2953fcSRajkumar Manoharan
ath10k_thermal_set_throttling(struct ath10k * ar)1268515b5c7SRajkumar Manoharan void ath10k_thermal_set_throttling(struct ath10k *ar)
1278515b5c7SRajkumar Manoharan {
1288515b5c7SRajkumar Manoharan u32 period, duration, enabled;
1298515b5c7SRajkumar Manoharan int ret;
1308515b5c7SRajkumar Manoharan
1318515b5c7SRajkumar Manoharan lockdep_assert_held(&ar->conf_mutex);
1328515b5c7SRajkumar Manoharan
13353884577SRakesh Pillai if (!test_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map))
13453884577SRakesh Pillai return;
13553884577SRakesh Pillai
136d600a6d7SMichal Kazior if (!ar->wmi.ops->gen_pdev_set_quiet_mode)
137d600a6d7SMichal Kazior return;
138d600a6d7SMichal Kazior
1399936fa59SRajkumar Manoharan if (ar->state != ATH10K_STATE_ON)
1409936fa59SRajkumar Manoharan return;
1419936fa59SRajkumar Manoharan
1428515b5c7SRajkumar Manoharan period = ar->thermal.quiet_period;
1438515b5c7SRajkumar Manoharan duration = (period * ar->thermal.throttle_state) / 100;
1448515b5c7SRajkumar Manoharan enabled = duration ? 1 : 0;
1458515b5c7SRajkumar Manoharan
1468515b5c7SRajkumar Manoharan ret = ath10k_wmi_pdev_set_quiet_mode(ar, period, duration,
1478515b5c7SRajkumar Manoharan ATH10K_QUIET_START_OFFSET,
1488515b5c7SRajkumar Manoharan enabled);
1498515b5c7SRajkumar Manoharan if (ret) {
1508515b5c7SRajkumar Manoharan ath10k_warn(ar, "failed to set quiet mode period %u duarion %u enabled %u ret %d\n",
1518515b5c7SRajkumar Manoharan period, duration, enabled, ret);
1528515b5c7SRajkumar Manoharan }
1538515b5c7SRajkumar Manoharan }
1548515b5c7SRajkumar Manoharan
ath10k_thermal_register(struct ath10k * ar)155fe6f36d6SRajkumar Manoharan int ath10k_thermal_register(struct ath10k *ar)
156fe6f36d6SRajkumar Manoharan {
157fe6f36d6SRajkumar Manoharan struct thermal_cooling_device *cdev;
158ac2953fcSRajkumar Manoharan struct device *hwmon_dev;
159fe6f36d6SRajkumar Manoharan int ret;
160fe6f36d6SRajkumar Manoharan
16153884577SRakesh Pillai if (!test_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map))
16253884577SRakesh Pillai return 0;
16353884577SRakesh Pillai
164fe6f36d6SRajkumar Manoharan cdev = thermal_cooling_device_register("ath10k_thermal", ar,
165fe6f36d6SRajkumar Manoharan &ath10k_thermal_ops);
166fe6f36d6SRajkumar Manoharan
167fe6f36d6SRajkumar Manoharan if (IS_ERR(cdev)) {
168fe6f36d6SRajkumar Manoharan ath10k_err(ar, "failed to setup thermal device result: %ld\n",
169fe6f36d6SRajkumar Manoharan PTR_ERR(cdev));
170fe6f36d6SRajkumar Manoharan return -EINVAL;
171fe6f36d6SRajkumar Manoharan }
172fe6f36d6SRajkumar Manoharan
173fe6f36d6SRajkumar Manoharan ret = sysfs_create_link(&ar->dev->kobj, &cdev->device.kobj,
174fe6f36d6SRajkumar Manoharan "cooling_device");
175fe6f36d6SRajkumar Manoharan if (ret) {
1767e47e8e3SRajkumar Manoharan ath10k_err(ar, "failed to create cooling device symlink\n");
177fe6f36d6SRajkumar Manoharan goto err_cooling_destroy;
178fe6f36d6SRajkumar Manoharan }
179fe6f36d6SRajkumar Manoharan
180fe6f36d6SRajkumar Manoharan ar->thermal.cdev = cdev;
18163fb32dfSRajkumar Manoharan ar->thermal.quiet_period = ATH10K_QUIET_PERIOD_DEFAULT;
182ac2953fcSRajkumar Manoharan
183ac2953fcSRajkumar Manoharan /* Do not register hwmon device when temperature reading is not
184ac2953fcSRajkumar Manoharan * supported by firmware
185ac2953fcSRajkumar Manoharan */
1866dd46348STamizh chelvam if (!(ar->wmi.ops->gen_pdev_get_temperature))
187fe6f36d6SRajkumar Manoharan return 0;
188fe6f36d6SRajkumar Manoharan
18996bba983SKalle Valo /* Avoid linking error on devm_hwmon_device_register_with_groups, I
190d6dfe25cSMarcin Rokicki * guess linux/hwmon.h is missing proper stubs.
191d6dfe25cSMarcin Rokicki */
192749bc03aSMasahiro Yamada if (!IS_REACHABLE(CONFIG_HWMON))
19396bba983SKalle Valo return 0;
19496bba983SKalle Valo
195ac2953fcSRajkumar Manoharan hwmon_dev = devm_hwmon_device_register_with_groups(ar->dev,
196ac2953fcSRajkumar Manoharan "ath10k_hwmon", ar,
197ac2953fcSRajkumar Manoharan ath10k_hwmon_groups);
198ac2953fcSRajkumar Manoharan if (IS_ERR(hwmon_dev)) {
199ac2953fcSRajkumar Manoharan ath10k_err(ar, "failed to register hwmon device: %ld\n",
200ac2953fcSRajkumar Manoharan PTR_ERR(hwmon_dev));
201ac2953fcSRajkumar Manoharan ret = -EINVAL;
202ac2953fcSRajkumar Manoharan goto err_remove_link;
203ac2953fcSRajkumar Manoharan }
204ac2953fcSRajkumar Manoharan return 0;
205ac2953fcSRajkumar Manoharan
206ac2953fcSRajkumar Manoharan err_remove_link:
2077e47e8e3SRajkumar Manoharan sysfs_remove_link(&ar->dev->kobj, "cooling_device");
208fe6f36d6SRajkumar Manoharan err_cooling_destroy:
209fe6f36d6SRajkumar Manoharan thermal_cooling_device_unregister(cdev);
210fe6f36d6SRajkumar Manoharan return ret;
211fe6f36d6SRajkumar Manoharan }
212fe6f36d6SRajkumar Manoharan
ath10k_thermal_unregister(struct ath10k * ar)213fe6f36d6SRajkumar Manoharan void ath10k_thermal_unregister(struct ath10k *ar)
214fe6f36d6SRajkumar Manoharan {
21553884577SRakesh Pillai if (!test_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map))
21653884577SRakesh Pillai return;
21753884577SRakesh Pillai
218fe6f36d6SRajkumar Manoharan sysfs_remove_link(&ar->dev->kobj, "cooling_device");
21983cfce87SMohammed Shafi Shajakhan thermal_cooling_device_unregister(ar->thermal.cdev);
220fe6f36d6SRajkumar Manoharan }
221