| From f2ef3b6850401af359d0446ca86d882ef2a96ce7 Mon Sep 17 00:00:00 2001 |
| From: Howard Hsu <howard-yh.hsu@mediatek.com> |
| Date: Thu, 2 Feb 2023 20:53:42 +0800 |
| Subject: [PATCH 13/29] wifi: mt76: mt7996: add thermal sensor device support |
| |
| --- |
| mt7996/init.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++ |
| mt7996/mcu.c | 41 ++++++++++++++++++++++++ |
| 2 files changed, 128 insertions(+) |
| |
| diff --git a/mt7996/init.c b/mt7996/init.c |
| index 44165a35..7350194f 100644 |
| --- a/mt7996/init.c |
| +++ b/mt7996/init.c |
| @@ -4,6 +4,8 @@ |
| */ |
| |
| #include <linux/etherdevice.h> |
| +#include <linux/hwmon.h> |
| +#include <linux/hwmon-sysfs.h> |
| #include <linux/thermal.h> |
| #include "mt7996.h" |
| #include "mac.h" |
| @@ -41,6 +43,81 @@ 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); |
| + |
| + if ((i - 1 == MT7996_CRIT_TEMP_IDX && |
| + val > phy->throttle_temp[MT7996_MAX_TEMP_IDX]) || |
| + (i - 1 == MT7996_MAX_TEMP_IDX && |
| + val < phy->throttle_temp[MT7996_CRIT_TEMP_IDX])) { |
| + dev_err(phy->dev->mt76.dev, |
| + "temp1_max shall be greater 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); |
| + 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) |
| @@ -112,6 +189,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", |
| @@ -130,6 +208,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; |
| } |
| |
| diff --git a/mt7996/mcu.c b/mt7996/mcu.c |
| index 3820a63e..b3326586 100644 |
| --- a/mt7996/mcu.c |
| +++ b/mt7996/mcu.c |
| @@ -3308,6 +3308,47 @@ 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) |
| { |
| -- |
| 2.39.2 |
| |