blob: ee6db36986ef3f4a4b02546ec63ebf3ec8a0f27d [file] [log] [blame]
developer8eb72a32023-03-30 08:32:07 +08001From 198d3e931e8f24534b63314334454f3254db7892 Mon Sep 17 00:00:00 2001
developer483388c2023-03-08 13:52:15 +08002From: Howard Hsu <howard-yh.hsu@mediatek.com>
3Date: Thu, 2 Feb 2023 21:20:31 +0800
developer1bc2ce22023-03-25 00:47:41 +08004Subject: [PATCH 12/29] wifi: mt76: mt7996: add thermal protection support
developer483388c2023-03-08 13:52:15 +08005
6This commit includes the following changes:
71. implement MTK thermal protection driver API
82. support Linux cooling device control
9
10Change-Id: I8fecc28f5b17ee50ae4644d1dd17d188dd694731
11---
12 mt76_connac_mcu.h | 1 +
13 mt7996/init.c | 105 +++++++++++++++++++++++++++++++++++++++++++++
14 mt7996/main.c | 8 ++++
15 mt7996/mcu.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++
16 mt7996/mcu.h | 44 +++++++++++++++++++
17 mt7996/mt7996.h | 15 +++++++
18 6 files changed, 279 insertions(+)
19
20diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
developer8eb72a32023-03-30 08:32:07 +080021index 6f30a0f..fa10d82 100644
developer483388c2023-03-08 13:52:15 +080022--- a/mt76_connac_mcu.h
23+++ b/mt76_connac_mcu.h
24@@ -1009,6 +1009,7 @@ enum {
25 MCU_UNI_EVENT_FW_LOG_2_HOST = 0x04,
26 MCU_UNI_EVENT_IE_COUNTDOWN = 0x09,
27 MCU_UNI_EVENT_RDD_REPORT = 0x11,
28+ MCU_UNI_EVENT_THERMAL = 0x35,
29 };
30
31 #define MCU_UNI_CMD_EVENT BIT(1)
32diff --git a/mt7996/init.c b/mt7996/init.c
developer8eb72a32023-03-30 08:32:07 +080033index de94e15..44165a3 100644
developer483388c2023-03-08 13:52:15 +080034--- a/mt7996/init.c
35+++ b/mt7996/init.c
36@@ -41,6 +41,98 @@ static const struct ieee80211_iface_combination if_comb[] = {
37 }
38 };
39
40+static int
41+mt7996_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev,
42+ unsigned long *state)
43+{
44+ *state = MT7996_CDEV_THROTTLE_MAX;
45+
46+ return 0;
47+}
48+
49+static int
50+mt7996_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev,
51+ unsigned long *state)
52+{
53+ struct mt7996_phy *phy = cdev->devdata;
54+
55+ *state = phy->cdev_state;
56+
57+ return 0;
58+}
59+
60+static int
61+mt7996_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev,
62+ unsigned long state)
63+{
64+ struct mt7996_phy *phy = cdev->devdata;
65+ u8 throttling = MT7996_THERMAL_THROTTLE_MAX - state;
66+ int ret;
67+
68+ if (state > MT7996_CDEV_THROTTLE_MAX) {
69+ dev_err(phy->dev->mt76.dev,
70+ "please specify a valid throttling state\n");
71+ return -EINVAL;
72+ }
73+
74+ if (state == phy->cdev_state)
75+ return 0;
76+
77+ /*
78+ * cooling_device convention: 0 = no cooling, more = more cooling
79+ * mcu convention: 1 = max cooling, more = less cooling
80+ */
81+ ret = mt7996_mcu_set_thermal_throttling(phy, throttling);
82+ if (ret)
83+ return ret;
84+
85+ phy->cdev_state = state;
86+
87+ return 0;
88+}
89+
90+static const struct thermal_cooling_device_ops mt7996_thermal_ops = {
91+ .get_max_state = mt7996_thermal_get_max_throttle_state,
92+ .get_cur_state = mt7996_thermal_get_cur_throttle_state,
93+ .set_cur_state = mt7996_thermal_set_cur_throttle_state,
94+};
95+
96+static void mt7996_unregister_thermal(struct mt7996_phy *phy)
97+{
98+ struct wiphy *wiphy = phy->mt76->hw->wiphy;
99+
100+ if (!phy->cdev)
101+ return;
102+
103+ sysfs_remove_link(&wiphy->dev.kobj, "cooling_device");
104+ thermal_cooling_device_unregister(phy->cdev);
105+}
106+
107+static int mt7996_thermal_init(struct mt7996_phy *phy)
108+{
109+ struct wiphy *wiphy = phy->mt76->hw->wiphy;
110+ struct thermal_cooling_device *cdev;
111+ const char *name;
112+
113+ name = devm_kasprintf(&wiphy->dev, GFP_KERNEL, "mt7996_%s",
114+ wiphy_name(wiphy));
115+
116+ cdev = thermal_cooling_device_register(name, phy, &mt7996_thermal_ops);
117+ if (!IS_ERR(cdev)) {
118+ if (sysfs_create_link(&wiphy->dev.kobj, &cdev->device.kobj,
119+ "cooling_device") < 0)
120+ thermal_cooling_device_unregister(cdev);
121+ else
122+ phy->cdev = cdev;
123+ }
124+
125+ /* initialize critical/maximum high temperature */
126+ phy->throttle_temp[MT7996_CRIT_TEMP_IDX] = MT7996_CRIT_TEMP;
127+ phy->throttle_temp[MT7996_MAX_TEMP_IDX] = MT7996_MAX_TEMP;
128+
129+ return 0;
130+}
131+
132 static void mt7996_led_set_config(struct led_classdev *led_cdev,
133 u8 delay_on, u8 delay_off)
134 {
developer1bc2ce22023-03-25 00:47:41 +0800135@@ -372,6 +464,10 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
developer483388c2023-03-08 13:52:15 +0800136 if (ret)
137 goto error;
138
139+ ret = mt7996_thermal_init(phy);
140+ if (ret)
141+ goto error;
142+
143 ret = mt7996_init_debugfs(phy);
144 if (ret)
145 goto error;
developer1bc2ce22023-03-25 00:47:41 +0800146@@ -392,6 +488,8 @@ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
developer483388c2023-03-08 13:52:15 +0800147 if (!phy)
148 return;
149
150+ mt7996_unregister_thermal(phy);
151+
152 mphy = phy->dev->mt76.phys[band];
153 mt76_unregister_phy(mphy);
154 ieee80211_free_hw(mphy->hw);
developer1bc2ce22023-03-25 00:47:41 +0800155@@ -881,6 +979,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
developer483388c2023-03-08 13:52:15 +0800156 if (ret)
157 return ret;
158
159+ ret = mt7996_thermal_init(&dev->phy);
160+ if (ret)
161+ return ret;
162+
163 ieee80211_queue_work(mt76_hw(dev), &dev->init_work);
164
165 ret = mt7996_register_phy(dev, mt7996_phy2(dev), MT_BAND1);
developer1bc2ce22023-03-25 00:47:41 +0800166@@ -898,6 +1000,9 @@ void mt7996_unregister_device(struct mt7996_dev *dev)
developer483388c2023-03-08 13:52:15 +0800167 {
168 mt7996_unregister_phy(mt7996_phy3(dev), MT_BAND2);
169 mt7996_unregister_phy(mt7996_phy2(dev), MT_BAND1);
170+
171+ mt7996_unregister_thermal(&dev->phy);
172+
173 mt76_unregister_device(&dev->mt76);
174 mt7996_mcu_exit(dev);
175 mt7996_tx_token_put(dev);
176diff --git a/mt7996/main.c b/mt7996/main.c
developer8eb72a32023-03-30 08:32:07 +0800177index 44d23e1..d8d578c 100644
developer483388c2023-03-08 13:52:15 +0800178--- a/mt7996/main.c
179+++ b/mt7996/main.c
180@@ -54,6 +54,14 @@ static int mt7996_start(struct ieee80211_hw *hw)
181 if (ret)
182 goto out;
183
184+ ret = mt7996_mcu_set_thermal_throttling(phy, MT7996_THERMAL_THROTTLE_MAX);
185+ if (ret)
186+ goto out;
187+
188+ ret = mt7996_mcu_set_thermal_protect(phy);
189+ if (ret)
190+ goto out;
191+
192 set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
193
194 ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
195diff --git a/mt7996/mcu.c b/mt7996/mcu.c
developer8eb72a32023-03-30 08:32:07 +0800196index b6bd36c..3820a63 100644
developer483388c2023-03-08 13:52:15 +0800197--- a/mt7996/mcu.c
198+++ b/mt7996/mcu.c
199@@ -443,6 +443,34 @@ mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
200 }
201 }
202
203+static void
204+mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb)
205+{
206+#define THERMAL_NOTIFY_TAG 0x4
207+#define THERMAL_NOTIFY 0x2
208+ struct mt76_phy *mphy = &dev->mt76.phy;
209+ struct mt7996_mcu_thermal_notify *n;
210+ struct mt7996_phy *phy;
211+
212+ n = (struct mt7996_mcu_thermal_notify *)skb->data;
213+
214+ if (n->tag != THERMAL_NOTIFY_TAG)
215+ return;
216+
217+ if (n->event_id != THERMAL_NOTIFY)
218+ return;
219+
220+ if (n->band_idx > MT_BAND2)
221+ return;
222+
223+ mphy = dev->mt76.phys[n->band_idx];
224+ if (!mphy)
225+ return;
226+
227+ phy = (struct mt7996_phy *)mphy->priv;
228+ phy->throttle_state = n->duty_percent;
229+}
230+
231 static void
232 mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb)
233 {
234@@ -487,6 +515,9 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
235 case MCU_UNI_EVENT_RDD_REPORT:
236 mt7996_mcu_rx_radar_detected(dev, skb);
237 break;
238+ case MCU_UNI_EVENT_THERMAL:
239+ mt7996_mcu_rx_thermal_notify(dev, skb);
240+ break;
241 default:
242 break;
243 }
244@@ -3277,6 +3308,81 @@ out:
245 return 0;
246 }
247
248+
249+int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state)
250+{
251+ struct {
252+ u8 _rsv[4];
253+
254+ __le16 tag;
255+ __le16 len;
256+
257+ struct mt7996_mcu_thermal_ctrl ctrl;
258+ } __packed req = {
259+ .tag = cpu_to_le16(UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG),
260+ .len = cpu_to_le16(sizeof(req) - 4),
261+ .ctrl = {
262+ .band_idx = phy->mt76->band_idx,
263+ },
264+ };
265+ int level, ret;
266+
267+ /* set duty cycle and level */
268+ for (level = 0; level < 4; level++) {
269+ req.ctrl.duty.duty_level = level;
270+ req.ctrl.duty.duty_cycle = state;
271+ state /= 2;
272+
273+ ret = mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL),
274+ &req, sizeof(req), false);
275+ if (ret)
276+ return ret;
277+ }
278+
279+ return 0;
280+}
281+
282+int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy)
283+{
284+#define SUSTAIN_PERIOD 10
285+ struct {
286+ u8 _rsv[4];
287+
288+ __le16 tag;
289+ __le16 len;
290+
291+ struct mt7996_mcu_thermal_ctrl ctrl;
292+ struct mt7996_mcu_thermal_enable enable;
293+ } __packed req = {
294+ .len = cpu_to_le16(sizeof(req) - 4 - sizeof(req.enable)),
295+ .ctrl = {
296+ .band_idx = phy->mt76->band_idx,
297+ .type.protect_type = 1,
298+ .type.trigger_type = 1,
299+ },
300+ };
301+ int ret;
302+
303+ req.tag = cpu_to_le16(UNI_CMD_THERMAL_PROTECT_DISABLE);
304+
305+ ret = mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL),
306+ &req, sizeof(req) - sizeof(req.enable), false);
307+ if (ret)
308+ return ret;
309+
310+ /* set high-temperature trigger threshold */
311+ req.tag = cpu_to_le16(UNI_CMD_THERMAL_PROTECT_ENABLE);
312+ /* add a safety margin ~10 */
313+ req.enable.restore_temp = cpu_to_le32(phy->throttle_temp[0] - 10);
314+ req.enable.trigger_temp = cpu_to_le32(phy->throttle_temp[1]);
315+ req.enable.sustain_time = cpu_to_le16(SUSTAIN_PERIOD);
316+
317+ req.len = cpu_to_le16(sizeof(req) - 4);
318+
319+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL),
320+ &req, sizeof(req), false);
321+}
322+
323 int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band)
324 {
325 struct {
326diff --git a/mt7996/mcu.h b/mt7996/mcu.h
developer8eb72a32023-03-30 08:32:07 +0800327index dd0c5ac..7fefc28 100644
developer483388c2023-03-08 13:52:15 +0800328--- a/mt7996/mcu.h
329+++ b/mt7996/mcu.h
330@@ -30,6 +30,28 @@ struct mt7996_mcu_uni_event {
331 __le32 status; /* 0: success, others: fail */
332 } __packed;
333
334+struct mt7996_mcu_thermal_ctrl {
335+ u8 ctrl_id;
336+ u8 band_idx;
337+ union {
338+ struct {
339+ u8 protect_type; /* 1: duty admit, 2: radio off */
340+ u8 trigger_type; /* 0: low, 1: high */
341+ } __packed type;
342+ struct {
343+ u8 duty_level; /* level 0~3 */
344+ u8 duty_cycle;
345+ } __packed duty;
346+ };
347+} __packed;
348+
349+struct mt7996_mcu_thermal_enable {
350+ __le32 trigger_temp;
351+ __le32 restore_temp;
352+ __le16 sustain_time;
353+ u8 rsv[2];
354+} __packed;
355+
356 struct mt7996_mcu_csa_notify {
357 struct mt7996_mcu_rxd rxd;
358
359@@ -153,6 +175,22 @@ struct mt7996_mcu_mib {
360 __le64 data;
361 } __packed;
362
363+struct mt7996_mcu_thermal_notify {
364+ struct mt7996_mcu_rxd rxd;
365+
366+ u8 __rsv1[4];
367+
368+ __le16 tag;
369+ __le16 len;
370+
371+ u8 event_id;
372+ u8 band_idx;
373+ u8 level_idx;
374+ u8 duty_percent;
375+ __le32 restore_temp;
376+ u8 __rsv2[4];
377+} __packed;
378+
379 enum mt7996_chan_mib_offs {
380 UNI_MIB_OBSS_AIRTIME = 26,
381 UNI_MIB_NON_WIFI_TIME = 27,
382@@ -642,6 +680,12 @@ enum{
383 UNI_CMD_SR_SET_SIGA = 0xd0,
384 };
385
386+enum {
387+ UNI_CMD_THERMAL_PROTECT_ENABLE = 0x6,
388+ UNI_CMD_THERMAL_PROTECT_DISABLE,
389+ UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG,
390+};
391+
392 enum {
393 UNI_CMD_ACCESS_REG_BASIC = 0x0,
394 UNI_CMD_ACCESS_RF_REG_BASIC,
395diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
developer8eb72a32023-03-30 08:32:07 +0800396index 997a0bf..25b20fa 100644
developer483388c2023-03-08 13:52:15 +0800397--- a/mt7996/mt7996.h
398+++ b/mt7996/mt7996.h
399@@ -43,6 +43,13 @@
400 #define MT7996_MAX_STA_TWT_AGRT 8
401 #define MT7996_MAX_QUEUE (__MT_RXQ_MAX + __MT_MCUQ_MAX + 3)
402
403+#define MT7996_THERMAL_THROTTLE_MAX 100
404+#define MT7996_CDEV_THROTTLE_MAX 99
405+#define MT7996_CRIT_TEMP_IDX 0
406+#define MT7996_MAX_TEMP_IDX 1
407+#define MT7996_CRIT_TEMP 110
408+#define MT7996_MAX_TEMP 120
409+
410 struct mt7996_vif;
411 struct mt7996_sta;
412 struct mt7996_dfs_pulse;
413@@ -211,6 +218,11 @@ struct mt7996_phy {
414
415 struct ieee80211_vif *monitor_vif;
416
417+ struct thermal_cooling_device *cdev;
418+ u8 cdev_state;
419+ u8 throttle_state;
420+ u32 throttle_temp[2]; /* 0: critical high, 1: maximum */
421+
422 u32 rxfilter;
423 u64 omac_mask;
424
425@@ -437,6 +449,9 @@ int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index,
426 int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable);
427 int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val);
428 int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch);
429+int mt7996_mcu_get_temperature(struct mt7996_phy *phy);
430+int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state);
431+int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy);
432 int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
433 u8 rx_sel, u8 val);
434 int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy,
435--
developer8eb72a32023-03-30 08:32:07 +08004362.18.0
developer483388c2023-03-08 13:52:15 +0800437