diff --git a/dp/wifi3.0/dp_internal.h b/dp/wifi3.0/dp_internal.h index 0c631c17924a..22da8713a5cf 100644 --- a/dp/wifi3.0/dp_internal.h +++ b/dp/wifi3.0/dp_internal.h @@ -1837,8 +1837,20 @@ void dp_peer_stats_update_protocol_cnt(struct cdp_soc_t *soc, #ifdef QCA_LL_TX_FLOW_CONTROL_V2 void dp_tx_dump_flow_pool_info(struct cdp_soc_t *soc_hdl); + +/** + * dp_tx_dump_flow_pool_info_compact() - dump flow pool info + * @soc: DP soc context + * + * Return: none + */ +void dp_tx_dump_flow_pool_info_compact(struct dp_soc *soc); int dp_tx_delete_flow_pool(struct dp_soc *soc, struct dp_tx_desc_pool_s *pool, bool force); +#else +static inline void dp_tx_dump_flow_pool_info_compact(struct dp_soc *soc) +{ +} #endif /* QCA_LL_TX_FLOW_CONTROL_V2 */ #ifdef QCA_OL_DP_SRNG_LOCK_LESS_ACCESS diff --git a/dp/wifi3.0/dp_main.c b/dp/wifi3.0/dp_main.c index 4f907821a850..c1615ed0da86 100644 --- a/dp/wifi3.0/dp_main.c +++ b/dp/wifi3.0/dp_main.c @@ -7503,7 +7503,7 @@ QDF_STATUS dp_reset_monitor_mode(struct cdp_soc_t *soc_hdl, * * Return: outstanding tx */ -static uint32_t dp_get_tx_pending(struct cdp_pdev *pdev_handle) +static int32_t dp_get_tx_pending(struct cdp_pdev *pdev_handle) { struct dp_pdev *pdev = (struct dp_pdev *)pdev_handle; @@ -10085,6 +10085,8 @@ static QDF_STATUS dp_txrx_dump_stats(struct cdp_soc_t *psoc, uint16_t value, case CDP_DUMP_TX_FLOW_POOL_INFO: if (level == QDF_STATS_VERBOSITY_LEVEL_HIGH) cdp_dump_flow_pool_info((struct cdp_soc_t *)soc); + else + dp_tx_dump_flow_pool_info_compact(soc); break; case CDP_DP_NAPI_STATS: @@ -11559,6 +11561,7 @@ static QDF_STATUS dp_runtime_suspend(struct cdp_soc_t *soc_hdl, uint8_t pdev_id) struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl); struct dp_pdev *pdev; uint8_t i; + int32_t tx_pending; pdev = dp_get_pdev_from_soc_pdev_id_wifi3(soc, pdev_id); if (!pdev) { @@ -11567,9 +11570,11 @@ static QDF_STATUS dp_runtime_suspend(struct cdp_soc_t *soc_hdl, uint8_t pdev_id) } /* Abort if there are any pending TX packets */ - if (dp_get_tx_pending(dp_pdev_to_cdp_pdev(pdev)) > 0) { + tx_pending = dp_get_tx_pending(dp_pdev_to_cdp_pdev(pdev)); + if (tx_pending) { QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO, - FL("Abort suspend due to pending TX packets")); + FL("Abort suspend due to pending TX packets %d"), + tx_pending); /* perform a force flush if tx is pending */ for (i = 0; i < soc->num_tcl_data_rings; i++) { @@ -12042,6 +12047,7 @@ static QDF_STATUS dp_bus_suspend(struct cdp_soc_t *soc_hdl, uint8_t pdev_id) struct dp_pdev *pdev = dp_get_pdev_from_soc_pdev_id_wifi3(soc, pdev_id); int timeout = SUSPEND_DRAIN_WAIT; int drain_wait_delay = 50; /* 50 ms */ + int32_t tx_pending; if (qdf_unlikely(!pdev)) { dp_err("pdev is NULL"); @@ -12049,10 +12055,11 @@ static QDF_STATUS dp_bus_suspend(struct cdp_soc_t *soc_hdl, uint8_t pdev_id) } /* Abort if there are any pending TX packets */ - while (dp_get_tx_pending((struct cdp_pdev *)pdev) > 0) { + while ((tx_pending = dp_get_tx_pending((struct cdp_pdev *)pdev))) { qdf_sleep(drain_wait_delay); if (timeout <= 0) { - dp_err("TX frames are pending, abort suspend"); + dp_info("TX frames are pending %d, abort suspend", + tx_pending); return QDF_STATUS_E_TIMEOUT; } timeout = timeout - drain_wait_delay; diff --git a/dp/wifi3.0/dp_rx.c b/dp/wifi3.0/dp_rx.c index 4b6615a75a63..9fd2fe6c87d0 100644 --- a/dp/wifi3.0/dp_rx.c +++ b/dp/wifi3.0/dp_rx.c @@ -1199,29 +1199,67 @@ static void dp_rx_fill_gro_info(struct dp_soc *soc, uint8_t *rx_tlv, * * @nbuf: pointer to msdu. * @mpdu_len: mpdu length + * @l3_pad_len: L3 padding length by HW * * Return: returns true if nbuf is last msdu of mpdu else retuns false. */ -static inline bool dp_rx_adjust_nbuf_len(qdf_nbuf_t nbuf, uint16_t *mpdu_len) +static inline bool dp_rx_adjust_nbuf_len(qdf_nbuf_t nbuf, + uint16_t *mpdu_len, + uint32_t l3_pad_len) { bool last_nbuf; + uint32_t pkt_hdr_size; - if (*mpdu_len > (RX_DATA_BUFFER_SIZE - RX_PKT_TLVS_LEN)) { + pkt_hdr_size = RX_PKT_TLVS_LEN + l3_pad_len; + + if ((*mpdu_len + pkt_hdr_size) > RX_DATA_BUFFER_SIZE) { qdf_nbuf_set_pktlen(nbuf, RX_DATA_BUFFER_SIZE); last_nbuf = false; + *mpdu_len -= (RX_DATA_BUFFER_SIZE - pkt_hdr_size); } else { - qdf_nbuf_set_pktlen(nbuf, (*mpdu_len + RX_PKT_TLVS_LEN)); + qdf_nbuf_set_pktlen(nbuf, (*mpdu_len + pkt_hdr_size)); last_nbuf = true; + *mpdu_len = 0; } - *mpdu_len -= (RX_DATA_BUFFER_SIZE - RX_PKT_TLVS_LEN); - return last_nbuf; } +/** + * dp_get_l3_hdr_pad_len() - get L3 header padding length. + * + * @soc: DP soc handle + * @nbuf: pointer to msdu. + * + * Return: returns padding length in bytes. + */ +static inline uint32_t dp_get_l3_hdr_pad_len(struct dp_soc *soc, + qdf_nbuf_t nbuf) +{ + uint32_t l3_hdr_pad = 0; + uint8_t *rx_tlv_hdr; + struct hal_rx_msdu_metadata msdu_metadata; + + while (nbuf) { + if (!qdf_nbuf_is_rx_chfrag_cont(nbuf)) { + /* scattered msdu end with continuation is 0 */ + rx_tlv_hdr = qdf_nbuf_data(nbuf); + hal_rx_msdu_metadata_get(soc->hal_soc, + rx_tlv_hdr, + &msdu_metadata); + l3_hdr_pad = msdu_metadata.l3_hdr_pad; + break; + } + nbuf = nbuf->next; + } + + return l3_hdr_pad; +} + /** * dp_rx_sg_create() - create a frag_list for MSDUs which are spread across * multiple nbufs. + * @soc: DP SOC handle * @nbuf: pointer to the first msdu of an amsdu. * * This function implements the creation of RX frag_list for cases @@ -1229,12 +1267,13 @@ static inline bool dp_rx_adjust_nbuf_len(qdf_nbuf_t nbuf, uint16_t *mpdu_len) * * Return: returns the head nbuf which contains complete frag_list. */ -qdf_nbuf_t dp_rx_sg_create(qdf_nbuf_t nbuf) +qdf_nbuf_t dp_rx_sg_create(struct dp_soc *soc, qdf_nbuf_t nbuf) { qdf_nbuf_t parent, frag_list, next = NULL; uint16_t frag_list_len = 0; uint16_t mpdu_len; bool last_nbuf; + uint32_t l3_hdr_pad_offset = 0; /* * Use msdu len got from REO entry descriptor instead since @@ -1242,6 +1281,7 @@ qdf_nbuf_t dp_rx_sg_create(qdf_nbuf_t nbuf) * from REO descriptor is right for non-raw RX scatter msdu. */ mpdu_len = QDF_NBUF_CB_RX_PKT_LEN(nbuf); + /* * this is a case where the complete msdu fits in one single nbuf. * in this case HW sets both start and end bit and we only need to @@ -1254,6 +1294,8 @@ qdf_nbuf_t dp_rx_sg_create(qdf_nbuf_t nbuf) return nbuf; } + l3_hdr_pad_offset = dp_get_l3_hdr_pad_len(soc, nbuf); + /* * This is a case where we have multiple msdus (A-MSDU) spread across * multiple nbufs. here we create a fraglist out of these nbufs. @@ -1273,7 +1315,24 @@ qdf_nbuf_t dp_rx_sg_create(qdf_nbuf_t nbuf) * nbufs will form the frag_list of the parent nbuf. */ qdf_nbuf_set_rx_chfrag_start(parent, 1); - last_nbuf = dp_rx_adjust_nbuf_len(parent, &mpdu_len); + /* + * L3 header padding is only needed for the 1st buffer + * in a scattered msdu + */ + last_nbuf = dp_rx_adjust_nbuf_len(parent, &mpdu_len, + l3_hdr_pad_offset); + + /* + * HW issue: MSDU cont bit is set but reported MPDU length can fit + * in to single buffer + * + * Increment error stats and avoid SG list creation + */ + if (last_nbuf) { + qdf_nbuf_pull_head(parent, + RX_PKT_TLVS_LEN + l3_hdr_pad_offset); + return parent; + } /* * this is where we set the length of the fragments which are @@ -1281,7 +1340,7 @@ qdf_nbuf_t dp_rx_sg_create(qdf_nbuf_t nbuf) * till we hit the last_nbuf of the list. */ do { - last_nbuf = dp_rx_adjust_nbuf_len(nbuf, &mpdu_len); + last_nbuf = dp_rx_adjust_nbuf_len(nbuf, &mpdu_len, 0); qdf_nbuf_pull_head(nbuf, RX_PKT_TLVS_LEN); frag_list_len += qdf_nbuf_len(nbuf); @@ -1298,7 +1357,8 @@ qdf_nbuf_t dp_rx_sg_create(qdf_nbuf_t nbuf) qdf_nbuf_append_ext_list(parent, frag_list, frag_list_len); parent->next = next; - qdf_nbuf_pull_head(parent, RX_PKT_TLVS_LEN); + qdf_nbuf_pull_head(parent, + RX_PKT_TLVS_LEN + l3_hdr_pad_offset); return parent; } @@ -2833,7 +2893,7 @@ done: qdf_nbuf_pull_head(nbuf, RX_PKT_TLVS_LEN); } else if (qdf_nbuf_is_rx_chfrag_cont(nbuf)) { msdu_len = QDF_NBUF_CB_RX_PKT_LEN(nbuf); - nbuf = dp_rx_sg_create(nbuf); + nbuf = dp_rx_sg_create(soc, nbuf); next = nbuf->next; if (qdf_nbuf_is_raw_frame(nbuf)) { diff --git a/dp/wifi3.0/dp_rx.h b/dp/wifi3.0/dp_rx.h index a5de867a7bbf..0818acf4aebf 100644 --- a/dp/wifi3.0/dp_rx.h +++ b/dp/wifi3.0/dp_rx.h @@ -676,6 +676,7 @@ dp_rx_wbm_err_process(struct dp_intr *int_ctx, struct dp_soc *soc, /** * dp_rx_sg_create() - create a frag_list for MSDUs which are spread across * multiple nbufs. + * @soc: core txrx main context * @nbuf: pointer to the first msdu of an amsdu. * * This function implements the creation of RX frag_list for cases @@ -683,7 +684,7 @@ dp_rx_wbm_err_process(struct dp_intr *int_ctx, struct dp_soc *soc, * * Return: returns the head nbuf which contains complete frag_list. */ -qdf_nbuf_t dp_rx_sg_create(qdf_nbuf_t nbuf); +qdf_nbuf_t dp_rx_sg_create(struct dp_soc *soc, qdf_nbuf_t nbuf); /* diff --git a/dp/wifi3.0/dp_rx_err.c b/dp/wifi3.0/dp_rx_err.c index 74c40099c526..dfd7f0b62e04 100644 --- a/dp/wifi3.0/dp_rx_err.c +++ b/dp/wifi3.0/dp_rx_err.c @@ -606,7 +606,7 @@ more_msdu_link_desc: rx_tlv_hdr_last = qdf_nbuf_data(tail_nbuf); if (qdf_unlikely(head_nbuf != tail_nbuf)) { - nbuf = dp_rx_sg_create(head_nbuf); + nbuf = dp_rx_sg_create(soc, head_nbuf); qdf_nbuf_set_is_frag(nbuf, 1); DP_STATS_INC(soc, rx.err.reo_err_oor_sg_count, 1); } @@ -2375,7 +2375,7 @@ done: * QCN9000 has this support */ if (qdf_nbuf_is_rx_chfrag_cont(nbuf)) { - nbuf = dp_rx_sg_create(nbuf); + nbuf = dp_rx_sg_create(soc, nbuf); next = nbuf->next; /* * SG error handling is not done correctly, diff --git a/dp/wifi3.0/dp_stats.c b/dp/wifi3.0/dp_stats.c index 86a3aa69f8bc..2d96e52c477c 100644 --- a/dp/wifi3.0/dp_stats.c +++ b/dp/wifi3.0/dp_stats.c @@ -5956,6 +5956,8 @@ void dp_txrx_path_stats(struct dp_soc *soc) pdev->stats.tx.dropped.fw_rem_notx); DP_PRINT_STATS("Invalid peer on tx path: %u", pdev->soc->stats.tx.tx_invalid_peer.num); + DP_PRINT_STATS("Tx desc freed in non-completion path: %u", + pdev->soc->stats.tx.tx_comp_exception); DP_PRINT_STATS("Tx packets sent per interrupt:"); DP_PRINT_STATS("Single Packet: %u", diff --git a/dp/wifi3.0/dp_tx.c b/dp/wifi3.0/dp_tx.c index 972fa3949b00..e54928cb66ca 100644 --- a/dp/wifi3.0/dp_tx.c +++ b/dp/wifi3.0/dp_tx.c @@ -4900,6 +4900,7 @@ more_data: !tx_desc->flags)) { dp_info_rl("Descriptor freed in vdev_detach %d", tx_desc_id); + DP_STATS_INC(soc, tx.tx_comp_exception, 1); continue; } diff --git a/dp/wifi3.0/dp_tx_flow_control.c b/dp/wifi3.0/dp_tx_flow_control.c index d14422294c4d..6d728d278d65 100644 --- a/dp/wifi3.0/dp_tx_flow_control.c +++ b/dp/wifi3.0/dp_tx_flow_control.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2021 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -29,6 +29,8 @@ #include "dp_internal.h" #define INVALID_FLOW_ID 0xFF #define MAX_INVALID_BIN 3 +#define GLOBAL_FLOW_POOL_STATS_LEN 25 +#define FLOW_POOL_LOG_LEN 50 #ifdef QCA_AC_BASED_FLOW_CONTROL /** @@ -222,6 +224,46 @@ void dp_tx_dump_flow_pool_info(struct cdp_soc_t *soc_hdl) qdf_spin_unlock_bh(&soc->flow_pool_array_lock); } +void dp_tx_dump_flow_pool_info_compact(struct dp_soc *soc) +{ + struct dp_txrx_pool_stats *pool_stats = &soc->pool_stats; + struct dp_tx_desc_pool_s *pool = NULL; + char *comb_log_str; + uint32_t comb_log_str_size; + int bytes_written = 0; + int i; + + comb_log_str_size = GLOBAL_FLOW_POOL_STATS_LEN + + (FLOW_POOL_LOG_LEN * MAX_TXDESC_POOLS) + 1; + comb_log_str = qdf_mem_malloc(comb_log_str_size); + if (!comb_log_str) + return; + + bytes_written = qdf_snprintf(&comb_log_str[bytes_written], + comb_log_str_size, "G:(%d,%d,%d) ", + pool_stats->pool_map_count, + pool_stats->pool_unmap_count, + pool_stats->pkt_drop_no_pool); + + for (i = 0; i < MAX_TXDESC_POOLS; i++) { + pool = &soc->tx_desc[i]; + if (pool->status > FLOW_POOL_INVALID) + continue; + bytes_written += qdf_snprintf(&comb_log_str[bytes_written], + (bytes_written >= comb_log_str_size) ? 0 : + comb_log_str_size - bytes_written, + "| %d %d: (%d,%d,%d)", + pool->flow_pool_id, pool->status, + pool->pool_size, pool->avail_desc, + pool->pkt_drop_no_desc); + } + + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_HIGH, + "FLOW_POOL_STATS %s", comb_log_str); + + qdf_mem_free(comb_log_str); +} + /** * dp_tx_clear_flow_pool_stats() - clear flow pool statistics * diff --git a/dp/wifi3.0/dp_types.h b/dp/wifi3.0/dp_types.h index 18bc9404d69d..2d3d0add270a 100644 --- a/dp/wifi3.0/dp_types.h +++ b/dp/wifi3.0/dp_types.h @@ -911,6 +911,8 @@ struct dp_soc_stats { uint32_t tx_comp_loop_pkt_limit_hit; /* Head pointer Out of sync at the end of dp_tx_comp_handler */ uint32_t hp_oos2; + /* tx desc freed as part of vdev detach */ + uint32_t tx_comp_exception; } tx; /* SOC level RX stats */ diff --git a/qdf/inc/qdf_trace.h b/qdf/inc/qdf_trace.h index f7bbe6a1717d..e60f4571de8c 100644 --- a/qdf/inc/qdf_trace.h +++ b/qdf/inc/qdf_trace.h @@ -1067,8 +1067,8 @@ enum qdf_dp_tx_rx_status qdf_dp_get_status_from_a_status(uint8_t status) void qdf_trace_display(void); -void __printf(3, 4) qdf_snprintf(char *str_buffer, unsigned int size, - char *str_format, ...); +int __printf(3, 4) qdf_snprintf(char *str_buffer, unsigned int size, + char *str_format, ...); #define QDF_SNPRINTF qdf_snprintf diff --git a/qdf/linux/src/qdf_trace.c b/qdf/linux/src/qdf_trace.c index db2746aa37fb..0b3e2dbd150d 100644 --- a/qdf/linux/src/qdf_trace.c +++ b/qdf/linux/src/qdf_trace.c @@ -110,11 +110,18 @@ static tp_qdf_dp_trace_cb qdf_dp_trace_cb_table[QDF_DP_TRACE_MAX + 1]; * string contains printf-like replacement parameters, which follow * this parameter in the variable argument list. * - * Return: None + * Return: num of bytes written to buffer */ -void qdf_snprintf(char *str_buffer, unsigned int size, char *str_format, ...) +int qdf_snprintf(char *str_buffer, unsigned int size, char *str_format, ...) { - snprintf(str_buffer, size, str_format); + va_list args; + int i; + + va_start(args, str_format); + i = vsnprintf(str_buffer, size, str_format, args); + va_end(args); + + return i; } qdf_export_symbol(qdf_snprintf); diff --git a/umac/mlme/vdev_mgr/core/src/vdev_mgr_ops.c b/umac/mlme/vdev_mgr/core/src/vdev_mgr_ops.c index 236d342bf993..9e1db6c55676 100644 --- a/umac/mlme/vdev_mgr/core/src/vdev_mgr_ops.c +++ b/umac/mlme/vdev_mgr/core/src/vdev_mgr_ops.c @@ -97,11 +97,10 @@ static bool vdev_mgr_is_opmode_sap_or_p2p_go(enum QDF_OPMODE op_mode) return (op_mode == QDF_SAP_MODE || op_mode == QDF_P2P_GO_MODE); } -static bool vdev_mgr_is_49G_5G_6G_chan_freq(uint16_t chan_freq) +static bool vdev_mgr_is_49G_5G_chan_freq(uint16_t chan_freq) { return WLAN_REG_IS_5GHZ_CH_FREQ(chan_freq) || - WLAN_REG_IS_49GHZ_FREQ(chan_freq) || - WLAN_REG_IS_6GHZ_CHAN_FREQ(chan_freq); + WLAN_REG_IS_49GHZ_FREQ(chan_freq); } #else static inline bool vdev_mgr_is_opmode_sap_or_p2p_go(enum QDF_OPMODE op_mode) @@ -109,7 +108,7 @@ static inline bool vdev_mgr_is_opmode_sap_or_p2p_go(enum QDF_OPMODE op_mode) return true; } -static inline bool vdev_mgr_is_49G_5G_6G_chan_freq(uint16_t chan_freq) +static inline bool vdev_mgr_is_49G_5G_chan_freq(uint16_t chan_freq) { return true; } @@ -151,7 +150,7 @@ static QDF_STATUS vdev_mgr_start_param_update( op_mode = wlan_vdev_mlme_get_opmode(vdev); if (vdev_mgr_is_opmode_sap_or_p2p_go(op_mode) && - vdev_mgr_is_49G_5G_6G_chan_freq(des_chan->ch_freq)) { + vdev_mgr_is_49G_5G_chan_freq(des_chan->ch_freq)) { tgt_dfs_set_current_channel_for_freq(pdev, des_chan->ch_freq, des_chan->ch_flags, des_chan->ch_flagext, diff --git a/umac/thermal/dispatcher/inc/wlan_thermal_public_struct.h b/umac/thermal/dispatcher/inc/wlan_thermal_public_struct.h new file mode 100644 index 000000000000..1c579241639c --- /dev/null +++ b/umac/thermal/dispatcher/inc/wlan_thermal_public_struct.h @@ -0,0 +1,60 @@ +/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: declare the thermal public structure + */ +#ifndef _WLAN_THERMAL_PUBLIC_STRUCT_H_ +#define _WLAN_THERMAL_PUBLIC_STRUCT_H_ + +/** + * enum thermal_throttle_level - firmware offload throttle level + * @THERMAL_FULLPERF: no any throtting + * @THERMAL_MITIGATION: throtting tx to do mitigation + * @THERMAL_SHUTOFF: shut down the tx completely + * @THERMAL_UNKNOWN: unknown level from target. + */ +enum thermal_throttle_level { + THERMAL_FULLPERF, + THERMAL_MITIGATION, + THERMAL_SHUTOFF, + THERMAL_UNKNOWN, +}; + +enum thermal_stats_request_type { + thermal_stats_none = 0, + thermal_stats_init, + thermal_stats_req, + thermal_stats_clear, + thermal_stats_current_all_sensors_temp, +}; + +/** + * thermal_throt_level_stats - thermal throttle info from Target + * @start_temp_level: Start temperature range to capture thermal stats + * @end_temp_level: End temperature range to capture thermal stats + * @total_time_ms_lo: Start time for every thermal stats level in msec + * @total_time_ms_hi: End time for every thermal stats level in msec + * @num_entry: Thermal stats counter for every time temp level for this range + */ +struct thermal_throt_level_stats { + uint32_t start_temp_level; + uint32_t end_temp_level; + uint32_t total_time_ms_lo; + uint32_t total_time_ms_hi; + uint32_t num_entry; +}; + +#endif /* _WLAN_THERMAL_PUBLIC_STRUCT_H_ */ diff --git a/wmi/inc/wmi_unified_api.h b/wmi/inc/wmi_unified_api.h index e77a24aff90a..6956d03061fb 100644 --- a/wmi/inc/wmi_unified_api.h +++ b/wmi/inc/wmi_unified_api.h @@ -32,6 +32,7 @@ #include "wlan_mgmt_txrx_utils_api.h" #include #include +#include "wlan_thermal_public_struct.h" #ifdef WLAN_POWER_MANAGEMENT_OFFLOAD #include "wmi_unified_pmo_api.h" #endif @@ -2965,13 +2966,19 @@ wmi_extract_chan_stats(wmi_unified_t wmi_handle, void *evt_buf, * @wmi_handle: wmi handle * @evt_buf: Pointer to event buffer * @temp: Pointer to hold extracted temperature - * @level: Pointer to hold extracted level + * @level: Pointer to hold extracted level in host enum + * @therm_throt_levels: Pointer to hold extracted number of level in thermal + * stats + * @tt_lvl_stats_event: Pointer to hold extracted thermal stats for each level * @pdev_id: Pointer to hold extracted pdev_id * * Return: QDF_STATUS_SUCCESS on success and QDF_STATUS_E_FAILURE for failure */ QDF_STATUS wmi_extract_thermal_stats(wmi_unified_t wmi_handle, void *evt_buf, - uint32_t *temp, uint32_t *level, + uint32_t *temp, + enum thermal_throttle_level *level, + uint32_t *therm_throt_levels, + struct thermal_throt_level_stats *tt_stats, uint32_t *pdev_id); /** diff --git a/wmi/inc/wmi_unified_fwol_api.h b/wmi/inc/wmi_unified_fwol_api.h index 478246ca3e64..d1546f839e5c 100644 --- a/wmi/inc/wmi_unified_fwol_api.h +++ b/wmi/inc/wmi_unified_fwol_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -87,4 +87,20 @@ wmi_unified_send_dscp_tip_map_cmd(struct wmi_unified *wmi_handle, } #endif /* WLAN_SEND_DSCP_UP_MAP_TO_FW */ +/** + * wmi_unified_send_get_thermal_stats_cmd() - Send WMI get thermal stats req + * @wmi_handle: wmi handle + * @req_type: Request type + * @temp_offset: temperature offset for setting the range for thermal stats + + * Send WMI get thermal stats req command to firmware. + * + * Return: QDF_STATUS + */ +#ifdef THERMAL_STATS_SUPPORT +QDF_STATUS +wmi_unified_send_get_thermal_stats_cmd(struct wmi_unified *wmi_handle, + enum thermal_stats_request_type req_type, + uint8_t temp_offset); +#endif /* THERMAL_STATS_SUPPORT */ #endif /* _WMI_UNIFIED_FWOL_API_H_ */ diff --git a/wmi/inc/wmi_unified_param.h b/wmi/inc/wmi_unified_param.h index 6b6ccabdd271..85fbd47c86de 100644 --- a/wmi/inc/wmi_unified_param.h +++ b/wmi/inc/wmi_unified_param.h @@ -5257,6 +5257,9 @@ typedef enum { #ifdef FEATURE_WLAN_TDLS wmi_service_tdls_ax_support, #endif +#endif +#ifdef THERMAL_STATS_SUPPORT + wmi_service_thermal_stats_temp_range_supported, #endif wmi_services_max, } wmi_conv_service_ids; diff --git a/wmi/inc/wmi_unified_priv.h b/wmi/inc/wmi_unified_priv.h index 22140bf64ead..6ed14454981a 100644 --- a/wmi/inc/wmi_unified_priv.h +++ b/wmi/inc/wmi_unified_priv.h @@ -1796,7 +1796,10 @@ QDF_STATUS (*extract_mib_stats)(wmi_unified_t wmi_handle, void *evt_buf, #endif QDF_STATUS (*extract_thermal_stats)(wmi_unified_t wmi_handle, void *evt_buf, - uint32_t *temp, uint32_t *level, uint32_t *pdev_id); + uint32_t *temp, enum thermal_throttle_level *level, + uint32_t *therm_throt_levels, + struct thermal_throt_level_stats *tt_temp_range_stats_event, + uint32_t *pdev_id); QDF_STATUS (*extract_thermal_level_stats)(wmi_unified_t wmi_handle, void *evt_buf, uint8_t idx, uint32_t *levelcount, @@ -2416,6 +2419,12 @@ QDF_STATUS (*send_dscp_tid_map_cmd)(wmi_unified_t wmi_handle, uint32_t *dscp_to_tid_map); #endif +#ifdef THERMAL_STATS_SUPPORT +QDF_STATUS (*send_get_thermal_stats_cmd)(wmi_unified_t wmi_handle, + enum thermal_stats_request_type req, + uint8_t temp_offset); +#endif /* THERMAL_STATS_SUPPORT */ + QDF_STATUS (*send_pdev_get_pn_cmd)(wmi_unified_t wmi_handle, struct peer_request_pn_param *pn_params); QDF_STATUS (*extract_get_pn_data)(wmi_unified_t wmi_handle, diff --git a/wmi/src/wmi_unified_api.c b/wmi/src/wmi_unified_api.c index c6057c075eea..71733a726529 100644 --- a/wmi/src/wmi_unified_api.c +++ b/wmi/src/wmi_unified_api.c @@ -2225,12 +2225,16 @@ wmi_extract_chan_stats(wmi_unified_t wmi_handle, void *evt_buf, } QDF_STATUS wmi_extract_thermal_stats(wmi_unified_t wmi_handle, void *evt_buf, - uint32_t *temp, uint32_t *level, + uint32_t *temp, + enum thermal_throttle_level *level, + uint32_t *therm_throt_levels, + struct thermal_throt_level_stats *tt_stats, uint32_t *pdev_id) { if (wmi_handle->ops->extract_thermal_stats) return wmi_handle->ops->extract_thermal_stats(wmi_handle, - evt_buf, temp, level, pdev_id); + evt_buf, temp, level, therm_throt_levels, + tt_stats, pdev_id); return QDF_STATUS_E_FAILURE; } diff --git a/wmi/src/wmi_unified_fwol_api.c b/wmi/src/wmi_unified_fwol_api.c index 7bca133af9f4..17f490b69454 100644 --- a/wmi/src/wmi_unified_fwol_api.c +++ b/wmi/src/wmi_unified_fwol_api.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -71,3 +71,18 @@ wmi_unified_send_dscp_tip_map_cmd(struct wmi_unified *wmi_handle, return QDF_STATUS_E_FAILURE; } #endif /* WLAN_SEND_DSCP_UP_MAP_TO_FW */ + +#ifdef THERMAL_STATS_SUPPORT +QDF_STATUS +wmi_unified_send_get_thermal_stats_cmd(struct wmi_unified *wmi_handle, + enum thermal_stats_request_type req_type, + uint8_t temp_offset) +{ + if (wmi_handle->ops->send_get_thermal_stats_cmd) + return wmi_handle->ops->send_get_thermal_stats_cmd(wmi_handle, + req_type, + temp_offset); + + return QDF_STATUS_E_FAILURE; +} +#endif /* THERMAL_STATS_SUPPORT */ diff --git a/wmi/src/wmi_unified_fwol_tlv.c b/wmi/src/wmi_unified_fwol_tlv.c index 39751fd83d52..6c0900dbfffb 100644 --- a/wmi/src/wmi_unified_fwol_tlv.c +++ b/wmi/src/wmi_unified_fwol_tlv.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -207,11 +207,65 @@ static void wmi_fwol_attach_dscp_tid_tlv(struct wmi_ops *ops) } #endif /* WLAN_SEND_DSCP_UP_MAP_TO_FW */ +#ifdef THERMAL_STATS_SUPPORT +/** + * send_get_thermal_stats_cmd_tlv() - send get thermal stats cmd to fw + * @wmi_handle: wmi handle + * @req_type: req type + * @temp_offset: temperature offset + * + * Send WMI_REQUEST_THERMAL_STATS_CMDID to fw. + * + * Return: QDF_STATUS + */ +static QDF_STATUS +send_get_thermal_stats_cmd_tlv(wmi_unified_t wmi_handle, + enum thermal_stats_request_type req_type, + uint8_t temp_offset) +{ + wmi_buf_t buf; + wmi_thermal_stats_cmd_fixed_param *cmd; + uint16_t len = sizeof(*cmd); + QDF_STATUS ret; + + buf = wmi_buf_alloc(wmi_handle, len); + if (!buf) { + wmi_err("Failed to allocate wmi buffer"); + return QDF_STATUS_E_NOMEM; + } + + cmd = (wmi_thermal_stats_cmd_fixed_param *)wmi_buf_data(buf); + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_thermal_stats_cmd_fixed_param, + WMITLV_GET_STRUCT_TLVLEN + (wmi_thermal_stats_cmd_fixed_param)); + cmd->thermal_action = req_type; + cmd->thermal_offset = temp_offset; + ret = wmi_unified_cmd_send(wmi_handle, buf, len, + WMI_REQUEST_THERMAL_STATS_CMDID); + if (QDF_IS_STATUS_ERROR(ret)) { + wmi_err("Failed to send get thermal stats cmd = %d", ret); + wmi_buf_free(buf); + } + + return ret; +} + +static void wmi_fwol_attach_thermal_stats_tlv(struct wmi_ops *ops) +{ + ops->send_get_thermal_stats_cmd = send_get_thermal_stats_cmd_tlv; +} +#else +static void wmi_fwol_attach_thermal_stats_tlv(struct wmi_ops *ops) +{ +} +#endif /* THERMAL_STATS_SUPPORT */ + void wmi_fwol_attach_tlv(wmi_unified_t wmi_handle) { struct wmi_ops *ops = wmi_handle->ops; wmi_fwol_attach_elna_tlv(ops); wmi_fwol_attach_dscp_tid_tlv(ops); - + wmi_fwol_attach_thermal_stats_tlv(ops); } diff --git a/wmi/src/wmi_unified_tlv.c b/wmi/src/wmi_unified_tlv.c index d0c332052ae6..76da4ea51604 100644 --- a/wmi/src/wmi_unified_tlv.c +++ b/wmi/src/wmi_unified_tlv.c @@ -11457,19 +11457,97 @@ static QDF_STATUS extract_scan_radio_cap_service_ready_ext2_tlv( return QDF_STATUS_SUCCESS; } +/** + * wmi_tgt_thermal_level_to_host() - Convert target thermal level to host enum + * @level: target thermal level from WMI_THERM_THROT_STATS_EVENTID event + * + * Return: host thermal throt level + */ +static enum thermal_throttle_level +wmi_tgt_thermal_level_to_host(uint32_t level) +{ + switch (level) { + case WMI_THERMAL_FULLPERF: + return THERMAL_FULLPERF; + case WMI_THERMAL_MITIGATION: + return THERMAL_MITIGATION; + case WMI_THERMAL_SHUTOFF: + return THERMAL_SHUTOFF; + default: + return THERMAL_UNKNOWN; + } +} + +#ifdef THERMAL_STATS_SUPPORT +static void +populate_thermal_stats(WMI_THERM_THROT_STATS_EVENTID_param_tlvs *param_buf, + uint32_t *therm_throt_levels, + struct thermal_throt_level_stats *tt_temp_range_stats) +{ + uint8_t lvl_idx; + wmi_therm_throt_stats_event_fixed_param *tt_stats_event; + wmi_thermal_throt_temp_range_stats *wmi_tt_stats; + + tt_stats_event = param_buf->fixed_param; + *therm_throt_levels = (tt_stats_event->therm_throt_levels > + WMI_THERMAL_STATS_TEMP_THRESH_LEVEL_MAX) ? + WMI_THERMAL_STATS_TEMP_THRESH_LEVEL_MAX : + tt_stats_event->therm_throt_levels; + + wmi_tt_stats = param_buf->temp_range_stats; + if (!wmi_tt_stats) { + wmi_err("wmi_tt_stats Null"); + return; + } + + for (lvl_idx = 0; lvl_idx < *therm_throt_levels; lvl_idx++) { + tt_temp_range_stats[lvl_idx].start_temp_level = + wmi_tt_stats[lvl_idx].start_temp_level; + tt_temp_range_stats[lvl_idx].end_temp_level = + wmi_tt_stats[lvl_idx].end_temp_level; + tt_temp_range_stats[lvl_idx].total_time_ms_lo = + wmi_tt_stats[lvl_idx].total_time_ms_lo; + tt_temp_range_stats[lvl_idx].total_time_ms_hi = + wmi_tt_stats[lvl_idx].total_time_ms_hi; + tt_temp_range_stats[lvl_idx].num_entry = + wmi_tt_stats[lvl_idx].num_entry; + wmi_debug("level %d, start temp %d, end temp %d, total time low %d, total time high %d, counter %d", + lvl_idx, wmi_tt_stats[lvl_idx].start_temp_level, + wmi_tt_stats[lvl_idx].end_temp_level, + wmi_tt_stats[lvl_idx].total_time_ms_lo, + wmi_tt_stats[lvl_idx].total_time_ms_hi, + wmi_tt_stats[lvl_idx].num_entry); + } +} +#else +static void +populate_thermal_stats(WMI_THERM_THROT_STATS_EVENTID_param_tlvs *param_buf, + uint32_t *therm_throt_levels, + struct thermal_throt_level_stats *tt_temp_range_stats) +{ +} +#endif + /** * extract_thermal_stats_tlv() - extract thermal stats from event * @wmi_handle: wmi handle * @param evt_buf: Pointer to event buffer * @param temp: Pointer to hold extracted temperature - * @param level: Pointer to hold extracted level + * @param level: Pointer to hold extracted level in host enum + * @param therm_throt_levels: Pointer to hold extracted thermal throttle temp + * range + * @param tt_temp_range_stats_event: Pointer to hold extracted thermal stats for + * every level * * Return: 0 for success or error code */ static QDF_STATUS extract_thermal_stats_tlv(wmi_unified_t wmi_handle, void *evt_buf, uint32_t *temp, - uint32_t *level, uint32_t *pdev_id) + enum thermal_throttle_level *level, + uint32_t *therm_throt_levels, + struct thermal_throt_level_stats *tt_temp_range_stats_event, + uint32_t *pdev_id) { WMI_THERM_THROT_STATS_EVENTID_param_tlvs *param_buf; wmi_therm_throt_stats_event_fixed_param *tt_stats_event; @@ -11480,12 +11558,17 @@ extract_thermal_stats_tlv(wmi_unified_t wmi_handle, return QDF_STATUS_E_INVAL; tt_stats_event = param_buf->fixed_param; - + wmi_debug("thermal temperature %d level %d", + tt_stats_event->temp, tt_stats_event->level); *pdev_id = wmi_handle->ops->convert_pdev_id_target_to_host( wmi_handle, tt_stats_event->pdev_id); *temp = tt_stats_event->temp; - *level = tt_stats_event->level; + *level = wmi_tgt_thermal_level_to_host(tt_stats_event->level); + + if (tt_stats_event->therm_throt_levels) + populate_thermal_stats(param_buf, therm_throt_levels, + tt_temp_range_stats_event); return QDF_STATUS_SUCCESS; } @@ -15762,6 +15845,10 @@ static void populate_tlv_service(uint32_t *wmi_service) #endif wmi_service[wmi_service_ampdu_tx_buf_size_256_support] = WMI_SERVICE_AMPDU_TX_BUF_SIZE_256_SUPPORT; +#ifdef THERMAL_STATS_SUPPORT + wmi_service[wmi_service_thermal_stats_temp_range_supported] = + WMI_SERVICE_THERMAL_THROT_STATS_TEMP_RANGE_SUPPORT; +#endif } /**