summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath/ath12k/thermal.c
blob: a764d2112a3cf1b548b15b4a0f138d759abade4a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
 * Copyright (c) 2020 The Linux Foundation. All rights reserved.
 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
 */

#include <linux/device.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/sysfs.h>
#include <linux/thermal.h>
#include "core.h"
#include "debug.h"

static ssize_t ath12k_thermal_temp_show(struct device *dev,
					struct device_attribute *attr,
					char *buf)
{
	struct ath12k *ar = dev_get_drvdata(dev);
	unsigned long time_left;
	int ret, temperature;

	guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy);

	if (ar->ah->state != ATH12K_HW_STATE_ON)
		return -ENETDOWN;

	reinit_completion(&ar->thermal.wmi_sync);
	ret = ath12k_wmi_send_pdev_temperature_cmd(ar);
	if (ret) {
		ath12k_warn(ar->ab, "failed to read temperature %d\n", ret);
		return ret;
	}

	if (test_bit(ATH12K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags))
		return -ESHUTDOWN;

	time_left = wait_for_completion_timeout(&ar->thermal.wmi_sync,
						ATH12K_THERMAL_SYNC_TIMEOUT_HZ);
	if (!time_left) {
		ath12k_warn(ar->ab, "failed to synchronize thermal read\n");
		return -ETIMEDOUT;
	}

	spin_lock_bh(&ar->data_lock);
	temperature = ar->thermal.temperature;
	spin_unlock_bh(&ar->data_lock);

	/* display in millidegree celsius */
	return sysfs_emit(buf, "%d\n", temperature * 1000);
}

void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature)
{
	spin_lock_bh(&ar->data_lock);
	ar->thermal.temperature = temperature;
	spin_unlock_bh(&ar->data_lock);
	complete_all(&ar->thermal.wmi_sync);
}

static SENSOR_DEVICE_ATTR_RO(temp1_input, ath12k_thermal_temp, 0);

static struct attribute *ath12k_hwmon_attrs[] = {
	&sensor_dev_attr_temp1_input.dev_attr.attr,
	NULL,
};
ATTRIBUTE_GROUPS(ath12k_hwmon);

int ath12k_thermal_register(struct ath12k_base *ab)
{
	struct ath12k *ar;
	int i, j, ret;

	if (!IS_REACHABLE(CONFIG_HWMON))
		return 0;

	for (i = 0; i < ab->num_radios; i++) {
		ar = ab->pdevs[i].ar;
		if (!ar)
			continue;

		ar->thermal.hwmon_dev =
			hwmon_device_register_with_groups(&ar->ah->hw->wiphy->dev,
							  "ath12k_hwmon", ar,
							  ath12k_hwmon_groups);
		if (IS_ERR(ar->thermal.hwmon_dev)) {
			ret = PTR_ERR(ar->thermal.hwmon_dev);
			ar->thermal.hwmon_dev = NULL;
			ath12k_err(ar->ab, "failed to register hwmon device: %d\n",
				   ret);
			for (j = i - 1; j >= 0; j--) {
				ar = ab->pdevs[j].ar;
				if (!ar)
					continue;

				hwmon_device_unregister(ar->thermal.hwmon_dev);
				ar->thermal.hwmon_dev = NULL;
			}
			return ret;
		}
	}

	return 0;
}

void ath12k_thermal_unregister(struct ath12k_base *ab)
{
	struct ath12k *ar;
	int i;

	if (!IS_REACHABLE(CONFIG_HWMON))
		return;

	for (i = 0; i < ab->num_radios; i++) {
		ar = ab->pdevs[i].ar;
		if (!ar)
			continue;

		if (ar->thermal.hwmon_dev) {
			hwmon_device_unregister(ar->thermal.hwmon_dev);
			ar->thermal.hwmon_dev = NULL;
		}
	}
}