wifi: mt76: mt7996: add thermal sensor device support
authorHoward Hsu <howard-yh.hsu@mediatek.com>
Mon, 23 Oct 2023 15:38:47 +0000 (23:38 +0800)
committerFelix Fietkau <nbd@nbd.name>
Thu, 7 Dec 2023 17:50:21 +0000 (18:50 +0100)
This patch adds support for thermal sensor device, including the
following features:
- Support to read current chip temperature.
- Support to set/get the trigger/restore temperature for thermal service.
- Support to read current chip tx cycle.

Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mt7996/init.c
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c

index 02b47b299ea8ae5ae1407a4d352fae3f9edc23af..e2d08bf96eb05b8517794dea97a3bd433340520b 100644 (file)
@@ -5,6 +5,8 @@
 
 #include <linux/etherdevice.h>
 #include <linux/of.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
 #include <linux/thermal.h>
 #include "mt7996.h"
 #include "mac.h"
@@ -43,6 +45,82 @@ static const struct ieee80211_iface_combination if_comb[] = {
        }
 };
 
+static ssize_t mt7996_thermal_temp_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct mt7996_phy *phy = dev_get_drvdata(dev);
+       int i = to_sensor_dev_attr(attr)->index;
+       int temperature;
+
+       switch (i) {
+       case 0:
+               temperature = mt7996_mcu_get_temperature(phy);
+               if (temperature < 0)
+                       return temperature;
+               /* display in millidegree celcius */
+               return sprintf(buf, "%u\n", temperature * 1000);
+       case 1:
+       case 2:
+               return sprintf(buf, "%u\n",
+                              phy->throttle_temp[i - 1] * 1000);
+       case 3:
+               return sprintf(buf, "%hhu\n", phy->throttle_state);
+       default:
+               return -EINVAL;
+       }
+}
+
+static ssize_t mt7996_thermal_temp_store(struct device *dev,
+                                        struct device_attribute *attr,
+                                        const char *buf, size_t count)
+{
+       struct mt7996_phy *phy = dev_get_drvdata(dev);
+       int ret, i = to_sensor_dev_attr(attr)->index;
+       long val;
+
+       ret = kstrtol(buf, 10, &val);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&phy->dev->mt76.mutex);
+       val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 40, 130);
+
+       /* add a safety margin ~10 */
+       if ((i - 1 == MT7996_CRIT_TEMP_IDX &&
+            val > phy->throttle_temp[MT7996_MAX_TEMP_IDX] - 10) ||
+           (i - 1 == MT7996_MAX_TEMP_IDX &&
+            val - 10 < phy->throttle_temp[MT7996_CRIT_TEMP_IDX])) {
+               dev_err(phy->dev->mt76.dev,
+                       "temp1_max shall be 10 degrees higher than temp1_crit.");
+               mutex_unlock(&phy->dev->mt76.mutex);
+               return -EINVAL;
+       }
+
+       phy->throttle_temp[i - 1] = val;
+       mutex_unlock(&phy->dev->mt76.mutex);
+
+       ret = mt7996_mcu_set_thermal_protect(phy, true);
+       if (ret)
+               return ret;
+
+       return count;
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, mt7996_thermal_temp, 0);
+static SENSOR_DEVICE_ATTR_RW(temp1_crit, mt7996_thermal_temp, 1);
+static SENSOR_DEVICE_ATTR_RW(temp1_max, mt7996_thermal_temp, 2);
+static SENSOR_DEVICE_ATTR_RO(throttle1, mt7996_thermal_temp, 3);
+
+static struct attribute *mt7996_hwmon_attrs[] = {
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit.dev_attr.attr,
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_throttle1.dev_attr.attr,
+       NULL,
+};
+ATTRIBUTE_GROUPS(mt7996_hwmon);
+
 static int
 mt7996_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev,
                                      unsigned long *state)
@@ -113,6 +191,7 @@ static int mt7996_thermal_init(struct mt7996_phy *phy)
 {
        struct wiphy *wiphy = phy->mt76->hw->wiphy;
        struct thermal_cooling_device *cdev;
+       struct device *hwmon;
        const char *name;
 
        name = devm_kasprintf(&wiphy->dev, GFP_KERNEL, "mt7996_%s",
@@ -131,6 +210,15 @@ static int mt7996_thermal_init(struct mt7996_phy *phy)
        phy->throttle_temp[MT7996_CRIT_TEMP_IDX] = MT7996_CRIT_TEMP;
        phy->throttle_temp[MT7996_MAX_TEMP_IDX] = MT7996_MAX_TEMP;
 
+       if (!IS_REACHABLE(CONFIG_HWMON))
+               return 0;
+
+       hwmon = devm_hwmon_device_register_with_groups(&wiphy->dev, name, phy,
+                                                      mt7996_hwmon_groups);
+
+       if (IS_ERR(hwmon))
+               return PTR_ERR(hwmon);
+
        return 0;
 }
 
index 292a6cd9b1fbdadbcd9a73bdeddb7a1f2c187837..48c7f4fe1bc7c98e6e7d68898727643c14d2470f 100644 (file)
@@ -3602,6 +3602,48 @@ out:
        return 0;
 }
 
+int mt7996_mcu_get_temperature(struct mt7996_phy *phy)
+{
+#define TEMPERATURE_QUERY 0
+#define GET_TEMPERATURE 0
+       struct {
+               u8 _rsv[4];
+
+               __le16 tag;
+               __le16 len;
+
+               u8 rsv1;
+               u8 action;
+               u8 band_idx;
+               u8 rsv2;
+       } req = {
+               .tag = cpu_to_le16(TEMPERATURE_QUERY),
+               .len = cpu_to_le16(sizeof(req) - 4),
+               .action = GET_TEMPERATURE,
+               .band_idx = phy->mt76->band_idx,
+       };
+       struct mt7996_mcu_thermal {
+               u8 _rsv[4];
+
+               __le16 tag;
+               __le16 len;
+
+               __le32 rsv;
+               __le32 temperature;
+       } __packed * res;
+       struct sk_buff *skb;
+       int ret;
+
+       ret = mt76_mcu_send_and_get_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL),
+                                       &req, sizeof(req), true, &skb);
+       if (ret)
+               return ret;
+
+       res = (void *)skb->data;
+
+       return le32_to_cpu(res->temperature);
+}
+
 int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state)
 {
        struct {