diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 32d590504a80..99e6cc6d6d7f 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -9664,6 +9664,12 @@ static int ath12k_mac_start(struct ath12k *ar) } } + ret = ath12k_thermal_throttling_config_default(ar); + if (ret) { + ath12k_err(ab, "failed to set thermal throttle: %d\n", ret); + goto err; + } + rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], &ab->pdevs[ar->pdev_idx]); @@ -14446,6 +14452,8 @@ static int ath12k_mac_setup_register(struct ath12k *ar, ar->rssi_info.temp_offset = 0; ar->rssi_info.noise_floor = ar->rssi_info.min_nf_dbm + ar->rssi_info.temp_offset; + ath12k_thermal_init_configs(ar); + return 0; } diff --git a/drivers/net/wireless/ath/ath12k/thermal.c b/drivers/net/wireless/ath/ath12k/thermal.c index a764d2112a3c..4f76622e8117 100644 --- a/drivers/net/wireless/ath/ath12k/thermal.c +++ b/drivers/net/wireless/ath/ath12k/thermal.c @@ -12,6 +12,70 @@ #include "core.h" #include "debug.h" +static const struct ath12k_wmi_tt_level_config_param +tt_level_configs[ATH12K_TT_CFG_IDX_MAX][ENHANCED_THERMAL_LEVELS] = { + [ATH12K_TT_CFG_IDX_IPA] = { + [0] = { .tmplwm = -100, .tmphwm = 115, .dcoffpercent = 0, + .pout_reduction_db = 0 }, + [1] = { .tmplwm = 110, .tmphwm = 120, .dcoffpercent = 0, + .pout_reduction_db = 12 }, + [2] = { .tmplwm = 115, .tmphwm = 125, .dcoffpercent = 50, + .pout_reduction_db = 12 }, + [3] = { .tmplwm = 120, .tmphwm = 130, .dcoffpercent = 90, + .pout_reduction_db = 12 }, + [4] = { .tmplwm = 125, .tmphwm = 130, .dcoffpercent = 100, + .pout_reduction_db = 12 }, + }, + [ATH12K_TT_CFG_IDX_XFEM] = { + [0] = { .tmplwm = -100, .tmphwm = 105, .dcoffpercent = 0, + .pout_reduction_db = 0 }, + [1] = { .tmplwm = 100, .tmphwm = 110, .dcoffpercent = 0, + .pout_reduction_db = 0 }, + [2] = { .tmplwm = 105, .tmphwm = 115, .dcoffpercent = 50, + .pout_reduction_db = 0 }, + [3] = { .tmplwm = 110, .tmphwm = 120, .dcoffpercent = 90, + .pout_reduction_db = 0 }, + [4] = { .tmplwm = 115, .tmphwm = 120, .dcoffpercent = 100, + .pout_reduction_db = 0 }, + }, +}; + +static enum ath12k_thermal_cfg_idx ath12k_thermal_cfg_index(struct ath12k *ar) +{ + if (test_bit(WMI_TLV_SERVICE_IS_TARGET_IPA, ar->ab->wmi_ab.svc_map)) + return ATH12K_TT_CFG_IDX_IPA; + + return ATH12K_TT_CFG_IDX_XFEM; +} + +int ath12k_thermal_throttling_config_default(struct ath12k *ar) +{ + struct ath12k_wmi_thermal_mitigation_arg param = {}; + int ret; + + if (test_bit(WMI_TLV_SERVICE_THERM_THROT_5_LEVELS, ar->ab->wmi_ab.svc_map)) + param.num_levels = ENHANCED_THERMAL_LEVELS; + else + param.num_levels = THERMAL_LEVELS; + + param.levelconf = ar->thermal.tt_level_configs; + + ret = ath12k_wmi_send_thermal_mitigation_cmd(ar, ¶m); + if (ret) + ath12k_warn(ar->ab, + "failed to send thermal mitigation cmd for default config: %d\n", + ret); + return ret; +} + +void ath12k_thermal_init_configs(struct ath12k *ar) +{ + enum ath12k_thermal_cfg_idx cfg_idx; + + cfg_idx = ath12k_thermal_cfg_index(ar); + ar->thermal.tt_level_configs = &tt_level_configs[cfg_idx][0]; +} + static ssize_t ath12k_thermal_temp_show(struct device *dev, struct device_attribute *attr, char *buf) diff --git a/drivers/net/wireless/ath/ath12k/thermal.h b/drivers/net/wireless/ath/ath12k/thermal.h index 9d84056188e1..33231bb3683c 100644 --- a/drivers/net/wireless/ath/ath12k/thermal.h +++ b/drivers/net/wireless/ath/ath12k/thermal.h @@ -9,18 +9,31 @@ #define ATH12K_THERMAL_SYNC_TIMEOUT_HZ (5 * HZ) +#define ATH12K_THERMAL_DEFAULT_DUTY_CYCLE 100 + +enum ath12k_thermal_cfg_idx { + /* Internal Power Amplifier Device */ + ATH12K_TT_CFG_IDX_IPA, + /* External Power Amplifier Device or External Front End Module */ + ATH12K_TT_CFG_IDX_XFEM, + ATH12K_TT_CFG_IDX_MAX, +}; + struct ath12k_thermal { struct completion wmi_sync; /* temperature value in Celsius degree protected by data_lock. */ int temperature; struct device *hwmon_dev; + const struct ath12k_wmi_tt_level_config_param *tt_level_configs; }; #if IS_REACHABLE(CONFIG_THERMAL) int ath12k_thermal_register(struct ath12k_base *ab); void ath12k_thermal_unregister(struct ath12k_base *ab); void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature); +int ath12k_thermal_throttling_config_default(struct ath12k *ar); +void ath12k_thermal_init_configs(struct ath12k *ar); #else static inline int ath12k_thermal_register(struct ath12k_base *ab) { @@ -36,5 +49,13 @@ static inline void ath12k_thermal_event_temperature(struct ath12k *ar, { } +static inline int ath12k_thermal_throttling_config_default(struct ath12k *ar) +{ + return 0; +} + +static inline void ath12k_thermal_init_configs(struct ath12k *ar) +{ +} #endif #endif /* _ATH12K_THERMAL_ */ diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 0a0c07ac0c48..c7559938564c 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -3380,6 +3380,75 @@ int ath12k_wmi_send_set_current_country_cmd(struct ath12k *ar, return ret; } +int +ath12k_wmi_send_thermal_mitigation_cmd(struct ath12k *ar, + struct ath12k_wmi_thermal_mitigation_arg *arg) +{ + struct ath12k_wmi_therm_throt_level_config_param *lvl_conf; + struct ath12k_wmi_therm_throt_config_request_cmd *cmd; + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct wmi_tlv *tlv; + struct sk_buff *skb; + int i, ret, len; + + len = sizeof(*cmd) + TLV_HDR_SIZE + (arg->num_levels * sizeof(*lvl_conf)); + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct ath12k_wmi_therm_throt_config_request_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_THERM_THROT_CONFIG_REQUEST, + sizeof(*cmd)); + cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id); + cmd->enable = cpu_to_le32(1); + cmd->dc = cpu_to_le32(100); + cmd->dc_per_event = cpu_to_le32(0xffffffff); + cmd->therm_throt_levels = cpu_to_le32(arg->num_levels); + + tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd)); + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, + arg->num_levels * sizeof(*lvl_conf)); + + lvl_conf = (struct ath12k_wmi_therm_throt_level_config_param *)tlv->value; + + for (i = 0; i < arg->num_levels; i++) { + lvl_conf->tlv_header = + ath12k_wmi_tlv_cmd_hdr(WMI_TAG_THERM_THROT_LEVEL_CONFIG_INFO, + sizeof(*lvl_conf)); + + lvl_conf->temp_lwm = a_cpu_to_sle32(arg->levelconf[i].tmplwm); + lvl_conf->temp_hwm = a_cpu_to_sle32(arg->levelconf[i].tmphwm); + lvl_conf->dc_off_percent = cpu_to_le32(arg->levelconf[i].dcoffpercent); + + if (test_bit(WMI_TLV_SERVICE_THERM_THROT_POUT_REDUCTION, + ar->ab->wmi_ab.svc_map)) + lvl_conf->pout_reduction_25db = + cpu_to_le32(arg->levelconf[i].pout_reduction_db); + + if (test_bit(WMI_TLV_SERVICE_THERM_THROT_TX_CHAIN_MASK, + ar->ab->wmi_ab.svc_map)) + lvl_conf->tx_chain_mask = cpu_to_le32(ar->cfg_tx_chainmask); + + lvl_conf->duty_cycle = cpu_to_le32(ATH12K_THERMAL_DEFAULT_DUTY_CYCLE); + lvl_conf++; + } + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "WMI vdev set thermal throt pdev_id %u enable dc 100 dc_per_event 0xffffffff levels %d\n", + ar->pdev->pdev_id, arg->num_levels); + + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_THERM_THROT_SET_CONF_CMDID); + if (ret) { + ath12k_warn(ar->ab, + "failed to send WMI_THERM_THROT_SET_CONF cmd: %d\n", + ret); + dev_kfree_skb(skb); + } + + return ret; +} + int ath12k_wmi_send_11d_scan_start_cmd(struct ath12k *ar, struct wmi_11d_scan_start_arg *arg) { diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index bbda2c441d0b..c644604c1426 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -2274,6 +2274,10 @@ enum wmi_tlv_service { WMI_TLV_SERVICE_WMSK_COMPACTION_RX_TLVS = 361, WMI_TLV_SERVICE_PEER_METADATA_V1A_V1B_SUPPORT = 365, + WMI_TLV_SERVICE_THERM_THROT_POUT_REDUCTION = 410, + WMI_TLV_SERVICE_IS_TARGET_IPA = 425, + WMI_TLV_SERVICE_THERM_THROT_TX_CHAIN_MASK = 426, + WMI_TLV_SERVICE_THERM_THROT_5_LEVELS = 429, WMI_TLV_SERVICE_ETH_OFFLOAD = 461, WMI_MAX_EXT2_SERVICE, @@ -4128,6 +4132,42 @@ struct wmi_therm_throt_stats_event { __le32 therm_throt_levels; } __packed; +#define THERMAL_LEVELS 4 +#define ENHANCED_THERMAL_LEVELS 5 + +struct ath12k_wmi_tt_level_config_param { + s32 tmplwm; + s32 tmphwm; + u32 dcoffpercent; + u32 pout_reduction_db; +}; + +struct ath12k_wmi_therm_throt_config_request_cmd { + __le32 tlv_header; + __le32 pdev_id; + __le32 enable; + __le32 dc; + /* After how many duty cycles the firmware sends stats to host */ + __le32 dc_per_event; + __le32 therm_throt_levels; +} __packed; + +struct ath12k_wmi_therm_throt_level_config_param { + __le32 tlv_header; + a_sle32 temp_lwm; + a_sle32 temp_hwm; + __le32 dc_off_percent; + __le32 prio; + __le32 pout_reduction_25db; + __le32 tx_chain_mask; + __le32 duty_cycle; +} __packed; + +struct ath12k_wmi_thermal_mitigation_arg { + int num_levels; + const struct ath12k_wmi_tt_level_config_param *levelconf; +}; + struct ath12k_wmi_init_country_arg { union { u16 country_code; @@ -6522,6 +6562,8 @@ __le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len); int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar, enum wmi_halphy_ctrl_path_stats_id tpc_stats_type); void ath12k_wmi_free_tpc_stats_mem(struct ath12k *ar); +int ath12k_wmi_send_thermal_mitigation_cmd(struct ath12k *ar, + struct ath12k_wmi_thermal_mitigation_arg *arg); static inline u32 ath12k_wmi_caps_ext_get_pdev_id(const struct ath12k_wmi_caps_ext_params *param)