wifi: mt76: connac: add thermal protection support for mt7996
Implement thermal protection commands and support Linux cooling device control for mt7996 chipsets. 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>
This commit is contained in:
parent
0afb228d9b
commit
21f290884b
@ -1021,6 +1021,7 @@ enum {
|
||||
MCU_UNI_EVENT_RDD_REPORT = 0x11,
|
||||
MCU_UNI_EVENT_ROC = 0x27,
|
||||
MCU_UNI_EVENT_TX_DONE = 0x2d,
|
||||
MCU_UNI_EVENT_THERMAL = 0x35,
|
||||
MCU_UNI_EVENT_NIC_CAPAB = 0x43,
|
||||
MCU_UNI_EVENT_WED_RRO = 0x57,
|
||||
MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
|
||||
|
@ -43,6 +43,97 @@ static const struct ieee80211_iface_combination if_comb[] = {
|
||||
}
|
||||
};
|
||||
|
||||
static int
|
||||
mt7996_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
*state = MT7996_CDEV_THROTTLE_MAX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mt7996_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct mt7996_phy *phy = cdev->devdata;
|
||||
|
||||
*state = phy->cdev_state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mt7996_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long state)
|
||||
{
|
||||
struct mt7996_phy *phy = cdev->devdata;
|
||||
u8 throttling = MT7996_THERMAL_THROTTLE_MAX - state;
|
||||
int ret;
|
||||
|
||||
if (state > MT7996_CDEV_THROTTLE_MAX) {
|
||||
dev_err(phy->dev->mt76.dev,
|
||||
"please specify a valid throttling state\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (state == phy->cdev_state)
|
||||
return 0;
|
||||
|
||||
/* cooling_device convention: 0 = no cooling, more = more cooling
|
||||
* mcu convention: 1 = max cooling, more = less cooling
|
||||
*/
|
||||
ret = mt7996_mcu_set_thermal_throttling(phy, throttling);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
phy->cdev_state = state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_cooling_device_ops mt7996_thermal_ops = {
|
||||
.get_max_state = mt7996_thermal_get_max_throttle_state,
|
||||
.get_cur_state = mt7996_thermal_get_cur_throttle_state,
|
||||
.set_cur_state = mt7996_thermal_set_cur_throttle_state,
|
||||
};
|
||||
|
||||
static void mt7996_unregister_thermal(struct mt7996_phy *phy)
|
||||
{
|
||||
struct wiphy *wiphy = phy->mt76->hw->wiphy;
|
||||
|
||||
if (!phy->cdev)
|
||||
return;
|
||||
|
||||
sysfs_remove_link(&wiphy->dev.kobj, "cooling_device");
|
||||
thermal_cooling_device_unregister(phy->cdev);
|
||||
}
|
||||
|
||||
static int mt7996_thermal_init(struct mt7996_phy *phy)
|
||||
{
|
||||
struct wiphy *wiphy = phy->mt76->hw->wiphy;
|
||||
struct thermal_cooling_device *cdev;
|
||||
const char *name;
|
||||
|
||||
name = devm_kasprintf(&wiphy->dev, GFP_KERNEL, "mt7996_%s",
|
||||
wiphy_name(wiphy));
|
||||
|
||||
cdev = thermal_cooling_device_register(name, phy, &mt7996_thermal_ops);
|
||||
if (!IS_ERR(cdev)) {
|
||||
if (sysfs_create_link(&wiphy->dev.kobj, &cdev->device.kobj,
|
||||
"cooling_device") < 0)
|
||||
thermal_cooling_device_unregister(cdev);
|
||||
else
|
||||
phy->cdev = cdev;
|
||||
}
|
||||
|
||||
/* initialize critical/maximum high temperature */
|
||||
phy->throttle_temp[MT7996_CRIT_TEMP_IDX] = MT7996_CRIT_TEMP;
|
||||
phy->throttle_temp[MT7996_MAX_TEMP_IDX] = MT7996_MAX_TEMP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mt7996_led_set_config(struct led_classdev *led_cdev,
|
||||
u8 delay_on, u8 delay_off)
|
||||
{
|
||||
@ -429,6 +520,10 @@ static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
ret = mt7996_thermal_init(phy);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
ret = mt7996_init_debugfs(phy);
|
||||
if (ret)
|
||||
goto error;
|
||||
@ -456,6 +551,8 @@ mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
|
||||
if (!phy)
|
||||
return;
|
||||
|
||||
mt7996_unregister_thermal(phy);
|
||||
|
||||
mphy = phy->dev->mt76.phys[band];
|
||||
mt76_unregister_phy(mphy);
|
||||
ieee80211_free_hw(mphy->hw);
|
||||
@ -1130,6 +1227,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mt7996_thermal_init(&dev->phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ieee80211_queue_work(mt76_hw(dev), &dev->init_work);
|
||||
|
||||
ret = mt7996_register_phy(dev, mt7996_phy2(dev), MT_BAND1);
|
||||
@ -1154,6 +1255,7 @@ void mt7996_unregister_device(struct mt7996_dev *dev)
|
||||
cancel_work_sync(&dev->wed_rro.work);
|
||||
mt7996_unregister_phy(mt7996_phy3(dev), MT_BAND2);
|
||||
mt7996_unregister_phy(mt7996_phy2(dev), MT_BAND1);
|
||||
mt7996_unregister_thermal(&dev->phy);
|
||||
mt7996_coredump_unregister(dev);
|
||||
mt76_unregister_device(&dev->mt76);
|
||||
mt7996_wed_rro_free(dev);
|
||||
|
@ -51,6 +51,14 @@ int mt7996_run(struct ieee80211_hw *hw)
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = mt7996_mcu_set_thermal_throttling(phy, MT7996_THERMAL_THROTTLE_MAX);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = mt7996_mcu_set_thermal_protect(phy, true);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
|
||||
|
||||
ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
|
||||
|
@ -497,6 +497,34 @@ mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb)
|
||||
{
|
||||
#define THERMAL_NOTIFY_TAG 0x4
|
||||
#define THERMAL_NOTIFY 0x2
|
||||
struct mt76_phy *mphy = &dev->mt76.phy;
|
||||
struct mt7996_mcu_thermal_notify *n;
|
||||
struct mt7996_phy *phy;
|
||||
|
||||
n = (struct mt7996_mcu_thermal_notify *)skb->data;
|
||||
|
||||
if (le16_to_cpu(n->tag) != THERMAL_NOTIFY_TAG)
|
||||
return;
|
||||
|
||||
if (n->event_id != THERMAL_NOTIFY)
|
||||
return;
|
||||
|
||||
if (n->band_idx > MT_BAND2)
|
||||
return;
|
||||
|
||||
mphy = dev->mt76.phys[n->band_idx];
|
||||
if (!mphy)
|
||||
return;
|
||||
|
||||
phy = (struct mt7996_phy *)mphy->priv;
|
||||
phy->throttle_state = n->duty_percent;
|
||||
}
|
||||
|
||||
static void
|
||||
mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb)
|
||||
{
|
||||
@ -520,6 +548,9 @@ mt7996_mcu_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
|
||||
case MCU_EVENT_EXT:
|
||||
mt7996_mcu_rx_ext_event(dev, skb);
|
||||
break;
|
||||
case MCU_UNI_EVENT_THERMAL:
|
||||
mt7996_mcu_rx_thermal_notify(dev, skb);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -3571,6 +3602,79 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state)
|
||||
{
|
||||
struct {
|
||||
u8 _rsv[4];
|
||||
|
||||
__le16 tag;
|
||||
__le16 len;
|
||||
|
||||
struct mt7996_mcu_thermal_ctrl ctrl;
|
||||
} __packed req = {
|
||||
.tag = cpu_to_le16(UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG),
|
||||
.len = cpu_to_le16(sizeof(req) - 4),
|
||||
.ctrl = {
|
||||
.band_idx = phy->mt76->band_idx,
|
||||
},
|
||||
};
|
||||
int level, ret;
|
||||
|
||||
/* set duty cycle and level */
|
||||
for (level = 0; level < 4; level++) {
|
||||
req.ctrl.duty.duty_level = level;
|
||||
req.ctrl.duty.duty_cycle = state;
|
||||
state /= 2;
|
||||
|
||||
ret = mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL),
|
||||
&req, sizeof(req), false);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable)
|
||||
{
|
||||
#define SUSTAIN_PERIOD 10
|
||||
struct {
|
||||
u8 _rsv[4];
|
||||
|
||||
__le16 tag;
|
||||
__le16 len;
|
||||
|
||||
struct mt7996_mcu_thermal_ctrl ctrl;
|
||||
struct mt7996_mcu_thermal_enable enable;
|
||||
} __packed req = {
|
||||
.len = cpu_to_le16(sizeof(req) - 4 - sizeof(req.enable)),
|
||||
.ctrl = {
|
||||
.band_idx = phy->mt76->band_idx,
|
||||
.type.protect_type = 1,
|
||||
.type.trigger_type = 1,
|
||||
},
|
||||
};
|
||||
int ret;
|
||||
|
||||
req.tag = cpu_to_le16(UNI_CMD_THERMAL_PROTECT_DISABLE);
|
||||
|
||||
ret = mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL),
|
||||
&req, sizeof(req) - sizeof(req.enable), false);
|
||||
if (ret || !enable)
|
||||
return ret;
|
||||
|
||||
/* set high-temperature trigger threshold */
|
||||
req.tag = cpu_to_le16(UNI_CMD_THERMAL_PROTECT_ENABLE);
|
||||
req.enable.restore_temp = cpu_to_le32(phy->throttle_temp[0]);
|
||||
req.enable.trigger_temp = cpu_to_le32(phy->throttle_temp[1]);
|
||||
req.enable.sustain_time = cpu_to_le16(SUSTAIN_PERIOD);
|
||||
|
||||
req.len = cpu_to_le16(sizeof(req) - 4);
|
||||
|
||||
return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL),
|
||||
&req, sizeof(req), false);
|
||||
}
|
||||
|
||||
int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band)
|
||||
{
|
||||
struct {
|
||||
|
@ -30,6 +30,28 @@ struct mt7996_mcu_uni_event {
|
||||
__le32 status; /* 0: success, others: fail */
|
||||
} __packed;
|
||||
|
||||
struct mt7996_mcu_thermal_ctrl {
|
||||
u8 ctrl_id;
|
||||
u8 band_idx;
|
||||
union {
|
||||
struct {
|
||||
u8 protect_type; /* 1: duty admit, 2: radio off */
|
||||
u8 trigger_type; /* 0: low, 1: high */
|
||||
} __packed type;
|
||||
struct {
|
||||
u8 duty_level; /* level 0~3 */
|
||||
u8 duty_cycle;
|
||||
} __packed duty;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
struct mt7996_mcu_thermal_enable {
|
||||
__le32 trigger_temp;
|
||||
__le32 restore_temp;
|
||||
__le16 sustain_time;
|
||||
u8 rsv[2];
|
||||
} __packed;
|
||||
|
||||
struct mt7996_mcu_csa_notify {
|
||||
struct mt7996_mcu_rxd rxd;
|
||||
|
||||
@ -214,6 +236,22 @@ enum {
|
||||
UNI_WED_RRO_BA_SESSION_DELETE,
|
||||
};
|
||||
|
||||
struct mt7996_mcu_thermal_notify {
|
||||
struct mt7996_mcu_rxd rxd;
|
||||
|
||||
u8 __rsv1[4];
|
||||
|
||||
__le16 tag;
|
||||
__le16 len;
|
||||
|
||||
u8 event_id;
|
||||
u8 band_idx;
|
||||
u8 level_idx;
|
||||
u8 duty_percent;
|
||||
__le32 restore_temp;
|
||||
u8 __rsv2[4];
|
||||
} __packed;
|
||||
|
||||
enum mt7996_chan_mib_offs {
|
||||
UNI_MIB_OBSS_AIRTIME = 26,
|
||||
UNI_MIB_NON_WIFI_TIME = 27,
|
||||
@ -719,6 +757,12 @@ enum{
|
||||
UNI_CMD_SR_SET_SIGA = 0xd0,
|
||||
};
|
||||
|
||||
enum {
|
||||
UNI_CMD_THERMAL_PROTECT_ENABLE = 0x6,
|
||||
UNI_CMD_THERMAL_PROTECT_DISABLE,
|
||||
UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG,
|
||||
};
|
||||
|
||||
enum {
|
||||
UNI_CMD_ACCESS_REG_BASIC = 0x0,
|
||||
UNI_CMD_ACCESS_RF_REG_BASIC,
|
||||
|
@ -50,6 +50,13 @@
|
||||
#define MT7996_BASIC_RATES_TBL 11
|
||||
#define MT7996_BEACON_RATES_TBL 25
|
||||
|
||||
#define MT7996_THERMAL_THROTTLE_MAX 100
|
||||
#define MT7996_CDEV_THROTTLE_MAX 99
|
||||
#define MT7996_CRIT_TEMP_IDX 0
|
||||
#define MT7996_MAX_TEMP_IDX 1
|
||||
#define MT7996_CRIT_TEMP 110
|
||||
#define MT7996_MAX_TEMP 120
|
||||
|
||||
#define MT7996_RRO_MAX_SESSION 1024
|
||||
#define MT7996_RRO_WINDOW_MAX_LEN 1024
|
||||
#define MT7996_RRO_ADDR_ELEM_LEN 128
|
||||
@ -195,6 +202,11 @@ struct mt7996_phy {
|
||||
|
||||
struct ieee80211_vif *monitor_vif;
|
||||
|
||||
struct thermal_cooling_device *cdev;
|
||||
u8 cdev_state;
|
||||
u8 throttle_state;
|
||||
u32 throttle_temp[2]; /* 0: critical high, 1: maximum */
|
||||
|
||||
u32 rxfilter;
|
||||
u64 omac_mask;
|
||||
|
||||
@ -453,6 +465,9 @@ int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable);
|
||||
int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val);
|
||||
int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif);
|
||||
int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch);
|
||||
int mt7996_mcu_get_temperature(struct mt7996_phy *phy);
|
||||
int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state);
|
||||
int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable);
|
||||
int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
|
||||
u8 rx_sel, u8 val);
|
||||
int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy,
|
||||
|
Loading…
x
Reference in New Issue
Block a user