xref: /openbmc/linux/drivers/net/wireless/ath/ath10k/thermal.c (revision 9144f784f852f9a125cabe9927b986d909bfa439)
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