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