
Make sure that the global dp tx desc pools are cleaned up only once during MLO umac reset. Change-Id: Id6e0ac6532b11ffb9ea190b6ab4d481fb486d853 CRs-Fixed: 3647660
1804 lines
48 KiB
C
1804 lines
48 KiB
C
/*
|
|
* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. 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.
|
|
*/
|
|
#include <wlan_utility.h>
|
|
#include <dp_internal.h>
|
|
#include <dp_htt.h>
|
|
#include <hal_be_api.h>
|
|
#include "dp_mlo.h"
|
|
#include <dp_be.h>
|
|
#include <dp_be_rx.h>
|
|
#include <dp_htt.h>
|
|
#include <dp_internal.h>
|
|
#include <wlan_cfg.h>
|
|
#include <wlan_mlo_mgr_cmn.h>
|
|
#include "dp_umac_reset.h"
|
|
|
|
#define dp_aggregate_vdev_stats_for_unmapped_peers(_tgtobj, _srcobj) \
|
|
DP_UPDATE_VDEV_STATS_FOR_UNMAPPED_PEERS(_tgtobj, _srcobj)
|
|
|
|
#ifdef DP_UMAC_HW_RESET_SUPPORT
|
|
/**
|
|
* dp_umac_reset_update_partner_map() - Update Umac reset partner map
|
|
* @mlo_ctx: mlo soc context
|
|
* @chip_id: chip id
|
|
* @set: flag indicating whether to set or clear the bit
|
|
*
|
|
* Return: void
|
|
*/
|
|
static void dp_umac_reset_update_partner_map(struct dp_mlo_ctxt *mlo_ctx,
|
|
int chip_id, bool set);
|
|
#endif
|
|
/**
|
|
* dp_mlo_ctxt_attach_wifi3() - Attach DP MLO context
|
|
* @ctrl_ctxt: CDP control context
|
|
*
|
|
* Return: DP MLO context handle on success, NULL on failure
|
|
*/
|
|
static struct cdp_mlo_ctxt *
|
|
dp_mlo_ctxt_attach_wifi3(struct cdp_ctrl_mlo_mgr *ctrl_ctxt)
|
|
{
|
|
struct dp_mlo_ctxt *mlo_ctxt =
|
|
qdf_mem_malloc(sizeof(struct dp_mlo_ctxt));
|
|
|
|
if (!mlo_ctxt) {
|
|
dp_err("Failed to allocate DP MLO Context");
|
|
return NULL;
|
|
}
|
|
|
|
mlo_ctxt->ctrl_ctxt = ctrl_ctxt;
|
|
|
|
if (dp_mlo_peer_find_hash_attach_be
|
|
(mlo_ctxt, DP_MAX_MLO_PEER) != QDF_STATUS_SUCCESS) {
|
|
dp_err("Failed to allocate peer hash");
|
|
qdf_mem_free(mlo_ctxt);
|
|
return NULL;
|
|
}
|
|
|
|
qdf_get_random_bytes(mlo_ctxt->toeplitz_hash_ipv4,
|
|
(sizeof(mlo_ctxt->toeplitz_hash_ipv4[0]) *
|
|
LRO_IPV4_SEED_ARR_SZ));
|
|
qdf_get_random_bytes(mlo_ctxt->toeplitz_hash_ipv6,
|
|
(sizeof(mlo_ctxt->toeplitz_hash_ipv6[0]) *
|
|
LRO_IPV6_SEED_ARR_SZ));
|
|
|
|
qdf_spinlock_create(&mlo_ctxt->ml_soc_list_lock);
|
|
qdf_spinlock_create(&mlo_ctxt->grp_umac_reset_ctx.grp_ctx_lock);
|
|
dp_mlo_dev_ctxt_list_attach(mlo_ctxt);
|
|
return dp_mlo_ctx_to_cdp(mlo_ctxt);
|
|
}
|
|
|
|
/**
|
|
* dp_mlo_ctxt_detach_wifi3() - Detach DP MLO context
|
|
* @cdp_ml_ctxt: pointer to CDP DP MLO context
|
|
*
|
|
* Return: void
|
|
*/
|
|
static void dp_mlo_ctxt_detach_wifi3(struct cdp_mlo_ctxt *cdp_ml_ctxt)
|
|
{
|
|
struct dp_mlo_ctxt *mlo_ctxt = cdp_mlo_ctx_to_dp(cdp_ml_ctxt);
|
|
|
|
if (!cdp_ml_ctxt)
|
|
return;
|
|
|
|
qdf_spinlock_destroy(&mlo_ctxt->grp_umac_reset_ctx.grp_ctx_lock);
|
|
qdf_spinlock_destroy(&mlo_ctxt->ml_soc_list_lock);
|
|
dp_mlo_dev_ctxt_list_detach(mlo_ctxt);
|
|
dp_mlo_peer_find_hash_detach_be(mlo_ctxt);
|
|
qdf_mem_free(mlo_ctxt);
|
|
}
|
|
|
|
/**
|
|
* dp_mlo_set_soc_by_chip_id() - Add DP soc to ML context soc list
|
|
* @ml_ctxt: DP ML context handle
|
|
* @soc: DP soc handle
|
|
* @chip_id: MLO chip id
|
|
*
|
|
* Return: void
|
|
*/
|
|
static void dp_mlo_set_soc_by_chip_id(struct dp_mlo_ctxt *ml_ctxt,
|
|
struct dp_soc *soc,
|
|
uint8_t chip_id)
|
|
{
|
|
qdf_spin_lock_bh(&ml_ctxt->ml_soc_list_lock);
|
|
ml_ctxt->ml_soc_list[chip_id] = soc;
|
|
|
|
/* The same API is called during soc_attach and soc_detach
|
|
* soc parameter is non-null or null accordingly.
|
|
*/
|
|
if (soc)
|
|
ml_ctxt->ml_soc_cnt++;
|
|
else
|
|
ml_ctxt->ml_soc_cnt--;
|
|
|
|
dp_umac_reset_update_partner_map(ml_ctxt, chip_id, !!soc);
|
|
|
|
qdf_spin_unlock_bh(&ml_ctxt->ml_soc_list_lock);
|
|
}
|
|
|
|
struct dp_soc*
|
|
dp_mlo_get_soc_ref_by_chip_id(struct dp_mlo_ctxt *ml_ctxt,
|
|
uint8_t chip_id)
|
|
{
|
|
struct dp_soc *soc = NULL;
|
|
|
|
if (!ml_ctxt) {
|
|
dp_warn("MLO context not created, MLO not enabled");
|
|
return NULL;
|
|
}
|
|
|
|
qdf_spin_lock_bh(&ml_ctxt->ml_soc_list_lock);
|
|
soc = ml_ctxt->ml_soc_list[chip_id];
|
|
|
|
if (!soc) {
|
|
qdf_spin_unlock_bh(&ml_ctxt->ml_soc_list_lock);
|
|
return NULL;
|
|
}
|
|
|
|
qdf_atomic_inc(&soc->ref_count);
|
|
qdf_spin_unlock_bh(&ml_ctxt->ml_soc_list_lock);
|
|
|
|
return soc;
|
|
}
|
|
|
|
static QDF_STATUS dp_partner_soc_rx_hw_cc_init(struct dp_mlo_ctxt *mlo_ctxt,
|
|
struct dp_soc_be *be_soc)
|
|
{
|
|
uint8_t i;
|
|
struct dp_soc *partner_soc;
|
|
struct dp_soc_be *be_partner_soc;
|
|
uint8_t pool_id;
|
|
QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
|
|
|
|
for (i = 0; i < WLAN_MAX_MLO_CHIPS; i++) {
|
|
partner_soc = dp_mlo_get_soc_ref_by_chip_id(mlo_ctxt, i);
|
|
if (!partner_soc) {
|
|
dp_err("partner_soc is NULL");
|
|
continue;
|
|
}
|
|
|
|
be_partner_soc = dp_get_be_soc_from_dp_soc(partner_soc);
|
|
|
|
for (pool_id = 0; pool_id < MAX_RXDESC_POOLS; pool_id++) {
|
|
qdf_status =
|
|
dp_hw_cookie_conversion_init
|
|
(be_soc,
|
|
&be_partner_soc->rx_cc_ctx[pool_id]);
|
|
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
|
|
dp_alert("MLO partner soc RX CC init failed");
|
|
return qdf_status;
|
|
}
|
|
}
|
|
}
|
|
|
|
return qdf_status;
|
|
}
|
|
|
|
static void dp_mlo_soc_drain_rx_buf(struct dp_soc *soc, void *arg, int chip_id)
|
|
{
|
|
uint8_t i = 0;
|
|
uint8_t cpu = 0;
|
|
uint8_t rx_ring_mask[WLAN_CFG_INT_NUM_CONTEXTS] = {0};
|
|
uint8_t rx_err_ring_mask[WLAN_CFG_INT_NUM_CONTEXTS] = {0};
|
|
uint8_t rx_wbm_rel_ring_mask[WLAN_CFG_INT_NUM_CONTEXTS] = {0};
|
|
uint8_t reo_status_ring_mask[WLAN_CFG_INT_NUM_CONTEXTS] = {0};
|
|
|
|
/* Save the current interrupt mask and disable the interrupts */
|
|
for (i = 0; i < wlan_cfg_get_num_contexts(soc->wlan_cfg_ctx); i++) {
|
|
rx_ring_mask[i] = soc->intr_ctx[i].rx_ring_mask;
|
|
rx_err_ring_mask[i] = soc->intr_ctx[i].rx_err_ring_mask;
|
|
rx_wbm_rel_ring_mask[i] = soc->intr_ctx[i].rx_wbm_rel_ring_mask;
|
|
reo_status_ring_mask[i] = soc->intr_ctx[i].reo_status_ring_mask;
|
|
|
|
soc->intr_ctx[i].rx_ring_mask = 0;
|
|
soc->intr_ctx[i].rx_err_ring_mask = 0;
|
|
soc->intr_ctx[i].rx_wbm_rel_ring_mask = 0;
|
|
soc->intr_ctx[i].reo_status_ring_mask = 0;
|
|
}
|
|
|
|
/* make sure dp_service_srngs not running on any of the CPU */
|
|
for (cpu = 0; cpu < NR_CPUS; cpu++) {
|
|
while (qdf_atomic_test_bit(cpu,
|
|
&soc->service_rings_running))
|
|
;
|
|
}
|
|
|
|
for (i = 0; i < wlan_cfg_get_num_contexts(soc->wlan_cfg_ctx); i++) {
|
|
uint8_t ring = 0;
|
|
uint32_t num_entries = 0;
|
|
hal_ring_handle_t hal_ring_hdl = NULL;
|
|
uint8_t rx_mask = wlan_cfg_get_rx_ring_mask(
|
|
soc->wlan_cfg_ctx, i);
|
|
uint8_t rx_err_mask = wlan_cfg_get_rx_err_ring_mask(
|
|
soc->wlan_cfg_ctx, i);
|
|
uint8_t rx_wbm_rel_mask = wlan_cfg_get_rx_wbm_rel_ring_mask(
|
|
soc->wlan_cfg_ctx, i);
|
|
|
|
if (rx_mask) {
|
|
/* iterate through each reo ring and process the buf */
|
|
for (ring = 0; ring < soc->num_reo_dest_rings; ring++) {
|
|
if (!(rx_mask & (1 << ring)))
|
|
continue;
|
|
|
|
hal_ring_hdl =
|
|
soc->reo_dest_ring[ring].hal_srng;
|
|
num_entries = hal_srng_get_num_entries(
|
|
soc->hal_soc,
|
|
hal_ring_hdl);
|
|
dp_rx_process_be(&soc->intr_ctx[i],
|
|
hal_ring_hdl,
|
|
ring,
|
|
num_entries);
|
|
}
|
|
}
|
|
|
|
/* Process REO Exception ring */
|
|
if (rx_err_mask) {
|
|
hal_ring_hdl = soc->reo_exception_ring.hal_srng;
|
|
num_entries = hal_srng_get_num_entries(
|
|
soc->hal_soc,
|
|
hal_ring_hdl);
|
|
|
|
dp_rx_err_process(&soc->intr_ctx[i], soc,
|
|
hal_ring_hdl, num_entries);
|
|
}
|
|
|
|
/* Process Rx WBM release ring */
|
|
if (rx_wbm_rel_mask) {
|
|
hal_ring_hdl = soc->rx_rel_ring.hal_srng;
|
|
num_entries = hal_srng_get_num_entries(
|
|
soc->hal_soc,
|
|
hal_ring_hdl);
|
|
|
|
dp_rx_wbm_err_process(&soc->intr_ctx[i], soc,
|
|
hal_ring_hdl, num_entries);
|
|
}
|
|
}
|
|
|
|
/* restore the interrupt mask */
|
|
for (i = 0; i < wlan_cfg_get_num_contexts(soc->wlan_cfg_ctx); i++) {
|
|
soc->intr_ctx[i].rx_ring_mask = rx_ring_mask[i];
|
|
soc->intr_ctx[i].rx_err_ring_mask = rx_err_ring_mask[i];
|
|
soc->intr_ctx[i].rx_wbm_rel_ring_mask = rx_wbm_rel_ring_mask[i];
|
|
soc->intr_ctx[i].reo_status_ring_mask = reo_status_ring_mask[i];
|
|
}
|
|
}
|
|
|
|
static void dp_mlo_soc_setup(struct cdp_soc_t *soc_hdl,
|
|
struct cdp_mlo_ctxt *cdp_ml_ctxt)
|
|
{
|
|
struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl);
|
|
struct dp_mlo_ctxt *mlo_ctxt = cdp_mlo_ctx_to_dp(cdp_ml_ctxt);
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
uint8_t pdev_id;
|
|
|
|
if (!cdp_ml_ctxt)
|
|
return;
|
|
|
|
be_soc->ml_ctxt = mlo_ctxt;
|
|
|
|
for (pdev_id = 0; pdev_id < MAX_PDEV_CNT; pdev_id++) {
|
|
if (soc->pdev_list[pdev_id])
|
|
dp_mlo_update_link_to_pdev_map(soc,
|
|
soc->pdev_list[pdev_id]);
|
|
}
|
|
|
|
dp_mlo_set_soc_by_chip_id(mlo_ctxt, soc, be_soc->mlo_chip_id);
|
|
}
|
|
|
|
static void dp_mlo_soc_teardown(struct cdp_soc_t *soc_hdl,
|
|
struct cdp_mlo_ctxt *cdp_ml_ctxt,
|
|
bool is_force_down)
|
|
{
|
|
struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl);
|
|
struct dp_mlo_ctxt *mlo_ctxt = cdp_mlo_ctx_to_dp(cdp_ml_ctxt);
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
|
|
if (!cdp_ml_ctxt)
|
|
return;
|
|
|
|
/* During the teardown drain the Rx buffers if any exist in the ring */
|
|
dp_mlo_iter_ptnr_soc(be_soc,
|
|
dp_mlo_soc_drain_rx_buf,
|
|
NULL);
|
|
|
|
dp_mlo_set_soc_by_chip_id(mlo_ctxt, NULL, be_soc->mlo_chip_id);
|
|
be_soc->ml_ctxt = NULL;
|
|
}
|
|
|
|
static void dp_mlo_setup_complete(struct cdp_mlo_ctxt *cdp_ml_ctxt)
|
|
{
|
|
struct dp_mlo_ctxt *mlo_ctxt = cdp_mlo_ctx_to_dp(cdp_ml_ctxt);
|
|
int i;
|
|
struct dp_soc *soc;
|
|
struct dp_soc_be *be_soc;
|
|
QDF_STATUS qdf_status;
|
|
|
|
if (!cdp_ml_ctxt)
|
|
return;
|
|
|
|
for (i = 0; i < WLAN_MAX_MLO_CHIPS; i++) {
|
|
soc = dp_mlo_get_soc_ref_by_chip_id(mlo_ctxt, i);
|
|
|
|
if (!soc)
|
|
continue;
|
|
be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
|
|
qdf_status = dp_partner_soc_rx_hw_cc_init(mlo_ctxt, be_soc);
|
|
|
|
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
|
|
dp_alert("MLO partner SOC Rx desc CC init failed");
|
|
qdf_assert_always(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void dp_mlo_update_delta_tsf2(struct cdp_soc_t *soc_hdl,
|
|
uint8_t pdev_id, uint64_t delta_tsf2)
|
|
{
|
|
struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl);
|
|
struct dp_pdev *pdev;
|
|
struct dp_pdev_be *be_pdev;
|
|
|
|
pdev = dp_get_pdev_from_soc_pdev_id_wifi3((struct dp_soc *)soc,
|
|
pdev_id);
|
|
if (!pdev) {
|
|
dp_err("pdev is NULL for pdev_id %u", pdev_id);
|
|
return;
|
|
}
|
|
|
|
be_pdev = dp_get_be_pdev_from_dp_pdev(pdev);
|
|
|
|
be_pdev->delta_tsf2 = delta_tsf2;
|
|
}
|
|
|
|
static void dp_mlo_update_delta_tqm(struct cdp_soc_t *soc_hdl,
|
|
uint64_t delta_tqm)
|
|
{
|
|
struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl);
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
|
|
be_soc->delta_tqm = delta_tqm;
|
|
}
|
|
|
|
static void dp_mlo_update_mlo_ts_offset(struct cdp_soc_t *soc_hdl,
|
|
uint64_t offset)
|
|
{
|
|
struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl);
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
|
|
be_soc->mlo_tstamp_offset = offset;
|
|
}
|
|
|
|
#ifdef CONFIG_MLO_SINGLE_DEV
|
|
/**
|
|
* dp_aggregate_vdev_basic_stats() - aggregate vdev basic stats
|
|
* @tgt_vdev_stats: target vdev buffer
|
|
* @src_vdev_stats: source vdev buffer
|
|
*
|
|
* return: void
|
|
*/
|
|
static inline
|
|
void dp_aggregate_vdev_basic_stats(
|
|
struct cdp_vdev_stats *tgt_vdev_stats,
|
|
struct dp_vdev_stats *src_vdev_stats)
|
|
{
|
|
DP_UPDATE_BASIC_STATS(tgt_vdev_stats, src_vdev_stats);
|
|
}
|
|
|
|
/**
|
|
* dp_aggregate_vdev_ingress_stats() - aggregate vdev ingress stats
|
|
* @tgt_vdev_stats: target vdev buffer
|
|
* @src_vdev_stats: source vdev buffer
|
|
* @xmit_type: xmit type of packet - MLD/Link
|
|
*
|
|
* return: void
|
|
*/
|
|
static inline
|
|
void dp_aggregate_vdev_ingress_stats(
|
|
struct cdp_vdev_stats *tgt_vdev_stats,
|
|
struct dp_vdev_stats *src_vdev_stats,
|
|
enum dp_pkt_xmit_type xmit_type)
|
|
{
|
|
/* Aggregate vdev ingress stats */
|
|
DP_UPDATE_LINK_VDEV_INGRESS_STATS(tgt_vdev_stats, src_vdev_stats,
|
|
xmit_type);
|
|
}
|
|
|
|
/**
|
|
* dp_aggregate_all_vdev_stats() - aggregate vdev ingress and unmap peer stats
|
|
* @tgt_vdev_stats: target vdev buffer
|
|
* @src_vdev_stats: source vdev buffer
|
|
* @xmit_type: xmit type of packet - MLD/Link
|
|
*
|
|
* return: void
|
|
*/
|
|
static inline
|
|
void dp_aggregate_all_vdev_stats(
|
|
struct cdp_vdev_stats *tgt_vdev_stats,
|
|
struct dp_vdev_stats *src_vdev_stats,
|
|
enum dp_pkt_xmit_type xmit_type)
|
|
{
|
|
dp_aggregate_vdev_ingress_stats(tgt_vdev_stats, src_vdev_stats,
|
|
xmit_type);
|
|
dp_aggregate_vdev_stats_for_unmapped_peers(tgt_vdev_stats,
|
|
src_vdev_stats);
|
|
}
|
|
|
|
/**
|
|
* dp_mlo_vdev_stats_aggr_bridge_vap() - aggregate bridge vdev stats
|
|
* @be_vdev: Dp Vdev handle
|
|
* @bridge_vdev: Dp vdev handle for bridge vdev
|
|
* @arg: buffer for target vdev stats
|
|
* @xmit_type: xmit type of packet - MLD/Link
|
|
*
|
|
* return: void
|
|
*/
|
|
static
|
|
void dp_mlo_vdev_stats_aggr_bridge_vap(struct dp_vdev_be *be_vdev,
|
|
struct dp_vdev *bridge_vdev,
|
|
void *arg,
|
|
enum dp_pkt_xmit_type xmit_type)
|
|
{
|
|
struct cdp_vdev_stats *tgt_vdev_stats = (struct cdp_vdev_stats *)arg;
|
|
struct dp_vdev_be *bridge_be_vdev = NULL;
|
|
|
|
bridge_be_vdev = dp_get_be_vdev_from_dp_vdev(bridge_vdev);
|
|
if (!bridge_be_vdev)
|
|
return;
|
|
|
|
dp_aggregate_all_vdev_stats(tgt_vdev_stats, &bridge_vdev->stats,
|
|
xmit_type);
|
|
dp_aggregate_vdev_stats_for_unmapped_peers(tgt_vdev_stats,
|
|
(&bridge_be_vdev->mlo_stats));
|
|
dp_vdev_iterate_peer(bridge_vdev, dp_update_vdev_stats, tgt_vdev_stats,
|
|
DP_MOD_ID_GENERIC_STATS);
|
|
}
|
|
|
|
/**
|
|
* dp_mlo_vdev_stats_aggr_bridge_vap_unified() - aggregate bridge vdev stats for
|
|
* unified mode, all MLO and legacy packets are submitted to vdev
|
|
* @be_vdev: Dp Vdev handle
|
|
* @bridge_vdev: Dp vdev handle for bridge vdev
|
|
* @arg: buffer for target vdev stats
|
|
*
|
|
* return: void
|
|
*/
|
|
static
|
|
void dp_mlo_vdev_stats_aggr_bridge_vap_unified(struct dp_vdev_be *be_vdev,
|
|
struct dp_vdev *bridge_vdev,
|
|
void *arg)
|
|
{
|
|
dp_mlo_vdev_stats_aggr_bridge_vap(be_vdev, bridge_vdev, arg,
|
|
DP_XMIT_TOTAL);
|
|
}
|
|
|
|
/**
|
|
* dp_mlo_vdev_stats_aggr_bridge_vap_mld() - aggregate bridge vdev stats for MLD
|
|
* mode, all MLO packets are submitted to MLD
|
|
* @be_vdev: Dp Vdev handle
|
|
* @bridge_vdev: Dp vdev handle for bridge vdev
|
|
* @arg: buffer for target vdev stats
|
|
*
|
|
* return: void
|
|
*/
|
|
static
|
|
void dp_mlo_vdev_stats_aggr_bridge_vap_mld(struct dp_vdev_be *be_vdev,
|
|
struct dp_vdev *bridge_vdev,
|
|
void *arg)
|
|
{
|
|
dp_mlo_vdev_stats_aggr_bridge_vap(be_vdev, bridge_vdev, arg,
|
|
DP_XMIT_MLD);
|
|
}
|
|
|
|
/**
|
|
* dp_aggregate_interface_stats_based_on_peer_type() - aggregate stats at
|
|
* VDEV level based on peer type connected to vdev
|
|
* @vdev: DP VDEV handle
|
|
* @vdev_stats: target vdev stats pointer
|
|
* @peer_type: type of peer - MLO Link or Legacy peer
|
|
*
|
|
* return: void
|
|
*/
|
|
static
|
|
void dp_aggregate_interface_stats_based_on_peer_type(
|
|
struct dp_vdev *vdev,
|
|
struct cdp_vdev_stats *vdev_stats,
|
|
enum dp_peer_type peer_type)
|
|
{
|
|
struct cdp_vdev_stats *tgt_vdev_stats = NULL;
|
|
struct dp_vdev_be *be_vdev = NULL;
|
|
struct dp_soc_be *be_soc = NULL;
|
|
|
|
if (!vdev || !vdev->pdev)
|
|
return;
|
|
|
|
tgt_vdev_stats = vdev_stats;
|
|
be_soc = dp_get_be_soc_from_dp_soc(vdev->pdev->soc);
|
|
be_vdev = dp_get_be_vdev_from_dp_vdev(vdev);
|
|
if (!be_vdev)
|
|
return;
|
|
|
|
if (peer_type == DP_PEER_TYPE_LEGACY) {
|
|
dp_aggregate_all_vdev_stats(tgt_vdev_stats,
|
|
&vdev->stats, DP_XMIT_LINK);
|
|
} else {
|
|
if (be_vdev->mcast_primary) {
|
|
dp_mlo_iter_ptnr_vdev(be_soc, be_vdev,
|
|
dp_mlo_vdev_stats_aggr_bridge_vap_mld,
|
|
(void *)vdev_stats,
|
|
DP_MOD_ID_GENERIC_STATS,
|
|
DP_BRIDGE_VDEV_ITER,
|
|
DP_VDEV_ITERATE_SKIP_SELF);
|
|
}
|
|
dp_aggregate_vdev_ingress_stats(tgt_vdev_stats,
|
|
&vdev->stats, DP_XMIT_MLD);
|
|
dp_aggregate_vdev_stats_for_unmapped_peers(
|
|
tgt_vdev_stats,
|
|
(&be_vdev->mlo_stats));
|
|
}
|
|
|
|
/* Aggregate associated peer stats */
|
|
dp_vdev_iterate_specific_peer_type(vdev,
|
|
dp_update_vdev_stats,
|
|
vdev_stats,
|
|
DP_MOD_ID_GENERIC_STATS,
|
|
peer_type);
|
|
}
|
|
|
|
/**
|
|
* dp_aggregate_interface_stats() - aggregate stats at VDEV level
|
|
* @vdev: DP VDEV handle
|
|
* @vdev_stats: target vdev stats pointer
|
|
*
|
|
* return: void
|
|
*/
|
|
static
|
|
void dp_aggregate_interface_stats(struct dp_vdev *vdev,
|
|
struct cdp_vdev_stats *vdev_stats)
|
|
{
|
|
struct dp_vdev_be *be_vdev = NULL;
|
|
struct dp_soc_be *be_soc = NULL;
|
|
|
|
if (!vdev || !vdev->pdev)
|
|
return;
|
|
|
|
be_soc = dp_get_be_soc_from_dp_soc(vdev->pdev->soc);
|
|
be_vdev = dp_get_be_vdev_from_dp_vdev(vdev);
|
|
if (!be_vdev)
|
|
return;
|
|
|
|
if (be_vdev->mcast_primary) {
|
|
dp_mlo_iter_ptnr_vdev(be_soc, be_vdev,
|
|
dp_mlo_vdev_stats_aggr_bridge_vap_unified,
|
|
(void *)vdev_stats, DP_MOD_ID_GENERIC_STATS,
|
|
DP_BRIDGE_VDEV_ITER,
|
|
DP_VDEV_ITERATE_SKIP_SELF);
|
|
}
|
|
|
|
dp_aggregate_vdev_stats_for_unmapped_peers(vdev_stats,
|
|
(&be_vdev->mlo_stats));
|
|
dp_aggregate_all_vdev_stats(vdev_stats, &vdev->stats,
|
|
DP_XMIT_TOTAL);
|
|
|
|
dp_vdev_iterate_peer(vdev, dp_update_vdev_stats, vdev_stats,
|
|
DP_MOD_ID_GENERIC_STATS);
|
|
|
|
dp_update_vdev_rate_stats(vdev_stats, &vdev->stats);
|
|
}
|
|
|
|
/**
|
|
* dp_mlo_aggr_ptnr_iface_stats() - aggregate mlo partner vdev stats
|
|
* @be_vdev: vdev handle
|
|
* @ptnr_vdev: partner vdev handle
|
|
* @arg: target buffer for aggregation
|
|
*
|
|
* return: void
|
|
*/
|
|
static
|
|
void dp_mlo_aggr_ptnr_iface_stats(struct dp_vdev_be *be_vdev,
|
|
struct dp_vdev *ptnr_vdev,
|
|
void *arg)
|
|
{
|
|
struct cdp_vdev_stats *tgt_vdev_stats = (struct cdp_vdev_stats *)arg;
|
|
|
|
dp_aggregate_interface_stats(ptnr_vdev, tgt_vdev_stats);
|
|
}
|
|
|
|
/**
|
|
* dp_mlo_aggr_ptnr_iface_stats_mlo_links() - aggregate mlo partner vdev stats
|
|
* based on peer type
|
|
* @be_vdev: vdev handle
|
|
* @ptnr_vdev: partner vdev handle
|
|
* @arg: target buffer for aggregation
|
|
*
|
|
* return: void
|
|
*/
|
|
static
|
|
void dp_mlo_aggr_ptnr_iface_stats_mlo_links(
|
|
struct dp_vdev_be *be_vdev,
|
|
struct dp_vdev *ptnr_vdev,
|
|
void *arg)
|
|
{
|
|
struct cdp_vdev_stats *tgt_vdev_stats = (struct cdp_vdev_stats *)arg;
|
|
|
|
dp_aggregate_interface_stats_based_on_peer_type(ptnr_vdev,
|
|
tgt_vdev_stats,
|
|
DP_PEER_TYPE_MLO_LINK);
|
|
}
|
|
|
|
/**
|
|
* dp_aggregate_sta_interface_stats() - for sta mode aggregate vdev stats from
|
|
* all link peers
|
|
* @soc: soc handle
|
|
* @vdev: vdev handle
|
|
* @buf: target buffer for aggregation
|
|
*
|
|
* return: QDF_STATUS
|
|
*/
|
|
static QDF_STATUS
|
|
dp_aggregate_sta_interface_stats(struct dp_soc *soc,
|
|
struct dp_vdev *vdev,
|
|
void *buf)
|
|
{
|
|
struct dp_peer *vap_bss_peer = NULL;
|
|
struct dp_peer *mld_peer = NULL;
|
|
struct dp_peer *link_peer = NULL;
|
|
struct dp_mld_link_peers link_peers_info;
|
|
uint8_t i = 0;
|
|
QDF_STATUS ret = QDF_STATUS_SUCCESS;
|
|
|
|
vap_bss_peer = dp_vdev_bss_peer_ref_n_get(soc, vdev,
|
|
DP_MOD_ID_GENERIC_STATS);
|
|
if (!vap_bss_peer)
|
|
return QDF_STATUS_E_FAILURE;
|
|
|
|
mld_peer = DP_GET_MLD_PEER_FROM_PEER(vap_bss_peer);
|
|
|
|
if (!mld_peer) {
|
|
dp_peer_unref_delete(vap_bss_peer, DP_MOD_ID_GENERIC_STATS);
|
|
return QDF_STATUS_E_FAILURE;
|
|
}
|
|
|
|
dp_get_link_peers_ref_from_mld_peer(soc, mld_peer, &link_peers_info,
|
|
DP_MOD_ID_GENERIC_STATS);
|
|
|
|
for (i = 0; i < link_peers_info.num_links; i++) {
|
|
link_peer = link_peers_info.link_peers[i];
|
|
dp_update_vdev_stats(soc, link_peer, buf);
|
|
dp_aggregate_vdev_ingress_stats((struct cdp_vdev_stats *)buf,
|
|
&link_peer->vdev->stats,
|
|
DP_XMIT_TOTAL);
|
|
dp_aggregate_vdev_basic_stats(
|
|
(struct cdp_vdev_stats *)buf,
|
|
&link_peer->vdev->stats);
|
|
}
|
|
|
|
dp_release_link_peers_ref(&link_peers_info, DP_MOD_ID_GENERIC_STATS);
|
|
dp_peer_unref_delete(vap_bss_peer, DP_MOD_ID_GENERIC_STATS);
|
|
return ret;
|
|
}
|
|
|
|
static QDF_STATUS dp_mlo_get_mld_vdev_stats(struct cdp_soc_t *soc_hdl,
|
|
uint8_t vdev_id, void *buf,
|
|
bool link_vdev_only)
|
|
{
|
|
struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl);
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_vdev *vdev = dp_vdev_get_ref_by_id(soc, vdev_id,
|
|
DP_MOD_ID_GENERIC_STATS);
|
|
struct dp_vdev_be *vdev_be = NULL;
|
|
QDF_STATUS ret = QDF_STATUS_SUCCESS;
|
|
|
|
if (!vdev)
|
|
return QDF_STATUS_E_FAILURE;
|
|
|
|
vdev_be = dp_get_be_vdev_from_dp_vdev(vdev);
|
|
if (!vdev_be || !vdev_be->mlo_dev_ctxt) {
|
|
dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_GENERIC_STATS);
|
|
return QDF_STATUS_E_FAILURE;
|
|
}
|
|
|
|
if (vdev->opmode == wlan_op_mode_sta) {
|
|
ret = dp_aggregate_sta_interface_stats(soc, vdev, buf);
|
|
goto complete;
|
|
}
|
|
|
|
if (DP_MLD_MODE_HYBRID_NONBOND == soc->mld_mode_ap &&
|
|
vdev->opmode == wlan_op_mode_ap) {
|
|
dp_aggregate_interface_stats_based_on_peer_type(
|
|
vdev, buf,
|
|
DP_PEER_TYPE_MLO_LINK);
|
|
if (link_vdev_only)
|
|
goto complete;
|
|
|
|
/* Aggregate stats from partner vdevs */
|
|
dp_mlo_iter_ptnr_vdev(be_soc, vdev_be,
|
|
dp_mlo_aggr_ptnr_iface_stats_mlo_links,
|
|
buf,
|
|
DP_MOD_ID_GENERIC_STATS,
|
|
DP_LINK_VDEV_ITER,
|
|
DP_VDEV_ITERATE_SKIP_SELF);
|
|
|
|
/* Aggregate vdev stats from MLO ctx for detached MLO Links */
|
|
dp_update_mlo_link_vdev_ctxt_stats(buf,
|
|
&vdev_be->mlo_dev_ctxt->stats,
|
|
DP_XMIT_MLD);
|
|
} else {
|
|
dp_aggregate_interface_stats(vdev, buf);
|
|
|
|
if (link_vdev_only)
|
|
goto complete;
|
|
|
|
/* Aggregate stats from partner vdevs */
|
|
dp_mlo_iter_ptnr_vdev(be_soc, vdev_be,
|
|
dp_mlo_aggr_ptnr_iface_stats, buf,
|
|
DP_MOD_ID_GENERIC_STATS,
|
|
DP_LINK_VDEV_ITER,
|
|
DP_VDEV_ITERATE_SKIP_SELF);
|
|
|
|
/* Aggregate vdev stats from MLO ctx for detached MLO Links */
|
|
dp_update_mlo_link_vdev_ctxt_stats(buf,
|
|
&vdev_be->mlo_dev_ctxt->stats,
|
|
DP_XMIT_TOTAL);
|
|
}
|
|
|
|
complete:
|
|
dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_GENERIC_STATS);
|
|
return ret;
|
|
}
|
|
|
|
QDF_STATUS
|
|
dp_get_interface_stats_be(struct cdp_soc_t *soc_hdl,
|
|
uint8_t vdev_id,
|
|
void *buf,
|
|
bool is_aggregate)
|
|
{
|
|
struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl);
|
|
struct dp_vdev *vdev = dp_vdev_get_ref_by_id(soc, vdev_id,
|
|
DP_MOD_ID_GENERIC_STATS);
|
|
if (!vdev)
|
|
return QDF_STATUS_E_FAILURE;
|
|
|
|
if (DP_MLD_MODE_HYBRID_NONBOND == soc->mld_mode_ap &&
|
|
vdev->opmode == wlan_op_mode_ap) {
|
|
dp_aggregate_interface_stats_based_on_peer_type(
|
|
vdev, buf,
|
|
DP_PEER_TYPE_LEGACY);
|
|
} else {
|
|
dp_aggregate_interface_stats(vdev, buf);
|
|
}
|
|
|
|
dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_GENERIC_STATS);
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
static struct cdp_mlo_ops dp_mlo_ops = {
|
|
.mlo_soc_setup = dp_mlo_soc_setup,
|
|
.mlo_soc_teardown = dp_mlo_soc_teardown,
|
|
.mlo_setup_complete = dp_mlo_setup_complete,
|
|
.mlo_update_delta_tsf2 = dp_mlo_update_delta_tsf2,
|
|
.mlo_update_delta_tqm = dp_mlo_update_delta_tqm,
|
|
.mlo_update_mlo_ts_offset = dp_mlo_update_mlo_ts_offset,
|
|
.mlo_ctxt_attach = dp_mlo_ctxt_attach_wifi3,
|
|
.mlo_ctxt_detach = dp_mlo_ctxt_detach_wifi3,
|
|
#ifdef CONFIG_MLO_SINGLE_DEV
|
|
.mlo_get_mld_vdev_stats = dp_mlo_get_mld_vdev_stats,
|
|
#endif
|
|
};
|
|
|
|
void dp_soc_mlo_fill_params(struct dp_soc *soc,
|
|
struct cdp_soc_attach_params *params)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
|
|
if (!params->mlo_enabled) {
|
|
dp_warn("MLO not enabled on SOC");
|
|
return;
|
|
}
|
|
|
|
be_soc->mlo_chip_id = params->mlo_chip_id;
|
|
be_soc->ml_ctxt = cdp_mlo_ctx_to_dp(params->ml_context);
|
|
be_soc->mlo_enabled = 1;
|
|
soc->cdp_soc.ops->mlo_ops = &dp_mlo_ops;
|
|
}
|
|
|
|
void dp_mlo_update_link_to_pdev_map(struct dp_soc *soc, struct dp_pdev *pdev)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_pdev_be *be_pdev = dp_get_be_pdev_from_dp_pdev(pdev);
|
|
struct dp_mlo_ctxt *ml_ctxt = be_soc->ml_ctxt;
|
|
uint8_t link_id;
|
|
|
|
if (!be_soc->mlo_enabled)
|
|
return;
|
|
|
|
if (!ml_ctxt)
|
|
return;
|
|
|
|
link_id = be_pdev->mlo_link_id;
|
|
|
|
if (link_id < WLAN_MAX_MLO_CHIPS * WLAN_MAX_MLO_LINKS_PER_SOC) {
|
|
if (!ml_ctxt->link_to_pdev_map[link_id])
|
|
ml_ctxt->link_to_pdev_map[link_id] = be_pdev;
|
|
else
|
|
dp_alert("Attempt to update existing map for link %u",
|
|
link_id);
|
|
}
|
|
}
|
|
|
|
void dp_mlo_update_link_to_pdev_unmap(struct dp_soc *soc, struct dp_pdev *pdev)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_pdev_be *be_pdev = dp_get_be_pdev_from_dp_pdev(pdev);
|
|
struct dp_mlo_ctxt *ml_ctxt = be_soc->ml_ctxt;
|
|
uint8_t link_id;
|
|
|
|
if (!be_soc->mlo_enabled)
|
|
return;
|
|
|
|
if (!ml_ctxt)
|
|
return;
|
|
|
|
link_id = be_pdev->mlo_link_id;
|
|
|
|
if (link_id < WLAN_MAX_MLO_CHIPS * WLAN_MAX_MLO_LINKS_PER_SOC)
|
|
ml_ctxt->link_to_pdev_map[link_id] = NULL;
|
|
}
|
|
|
|
static struct dp_pdev_be *
|
|
dp_mlo_get_be_pdev_from_link_id(struct dp_mlo_ctxt *ml_ctxt, uint8_t link_id)
|
|
{
|
|
if (link_id < WLAN_MAX_MLO_CHIPS * WLAN_MAX_MLO_LINKS_PER_SOC)
|
|
return ml_ctxt->link_to_pdev_map[link_id];
|
|
return NULL;
|
|
}
|
|
|
|
void dp_pdev_mlo_fill_params(struct dp_pdev *pdev,
|
|
struct cdp_pdev_attach_params *params)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(pdev->soc);
|
|
struct dp_pdev_be *be_pdev = dp_get_be_pdev_from_dp_pdev(pdev);
|
|
|
|
if (!be_soc->mlo_enabled) {
|
|
dp_info("MLO not enabled on SOC");
|
|
return;
|
|
}
|
|
|
|
be_pdev->mlo_link_id = params->mlo_link_id;
|
|
}
|
|
|
|
void dp_mlo_partner_chips_map(struct dp_soc *soc,
|
|
struct dp_peer *peer,
|
|
uint16_t peer_id)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *mlo_ctxt = NULL;
|
|
bool is_ml_peer_id =
|
|
HTT_RX_PEER_META_DATA_V1_ML_PEER_VALID_GET(peer_id);
|
|
uint8_t chip_id;
|
|
struct dp_soc *temp_soc;
|
|
|
|
/* for non ML peer dont map on partner chips*/
|
|
if (!is_ml_peer_id)
|
|
return;
|
|
|
|
mlo_ctxt = be_soc->ml_ctxt;
|
|
if (!mlo_ctxt)
|
|
return;
|
|
|
|
qdf_spin_lock_bh(&mlo_ctxt->ml_soc_list_lock);
|
|
for (chip_id = 0; chip_id < DP_MAX_MLO_CHIPS; chip_id++) {
|
|
temp_soc = mlo_ctxt->ml_soc_list[chip_id];
|
|
|
|
if (!temp_soc)
|
|
continue;
|
|
|
|
/* skip if this is current soc */
|
|
if (temp_soc == soc)
|
|
continue;
|
|
|
|
dp_peer_find_id_to_obj_add(temp_soc, peer, peer_id);
|
|
}
|
|
qdf_spin_unlock_bh(&mlo_ctxt->ml_soc_list_lock);
|
|
}
|
|
|
|
qdf_export_symbol(dp_mlo_partner_chips_map);
|
|
|
|
void dp_mlo_partner_chips_unmap(struct dp_soc *soc,
|
|
uint16_t peer_id)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *mlo_ctxt = be_soc->ml_ctxt;
|
|
bool is_ml_peer_id =
|
|
HTT_RX_PEER_META_DATA_V1_ML_PEER_VALID_GET(peer_id);
|
|
uint8_t chip_id;
|
|
struct dp_soc *temp_soc;
|
|
|
|
if (!is_ml_peer_id)
|
|
return;
|
|
|
|
if (!mlo_ctxt)
|
|
return;
|
|
|
|
qdf_spin_lock_bh(&mlo_ctxt->ml_soc_list_lock);
|
|
for (chip_id = 0; chip_id < DP_MAX_MLO_CHIPS; chip_id++) {
|
|
temp_soc = mlo_ctxt->ml_soc_list[chip_id];
|
|
|
|
if (!temp_soc)
|
|
continue;
|
|
|
|
/* skip if this is current soc */
|
|
if (temp_soc == soc)
|
|
continue;
|
|
|
|
dp_peer_find_id_to_obj_remove(temp_soc, peer_id);
|
|
}
|
|
qdf_spin_unlock_bh(&mlo_ctxt->ml_soc_list_lock);
|
|
}
|
|
|
|
qdf_export_symbol(dp_mlo_partner_chips_unmap);
|
|
|
|
uint8_t dp_mlo_get_chip_id(struct dp_soc *soc)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
|
|
return be_soc->mlo_chip_id;
|
|
}
|
|
|
|
qdf_export_symbol(dp_mlo_get_chip_id);
|
|
|
|
struct dp_peer *
|
|
dp_mlo_link_peer_hash_find_by_chip_id(struct dp_soc *soc,
|
|
uint8_t *peer_mac_addr,
|
|
int mac_addr_is_aligned,
|
|
uint8_t vdev_id,
|
|
uint8_t chip_id,
|
|
enum dp_mod_id mod_id)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *mlo_ctxt = be_soc->ml_ctxt;
|
|
struct dp_soc *link_peer_soc = NULL;
|
|
struct dp_peer *peer = NULL;
|
|
|
|
if (!mlo_ctxt)
|
|
return NULL;
|
|
|
|
link_peer_soc = dp_mlo_get_soc_ref_by_chip_id(mlo_ctxt, chip_id);
|
|
|
|
if (!link_peer_soc)
|
|
return NULL;
|
|
|
|
peer = dp_peer_find_hash_find(link_peer_soc, peer_mac_addr,
|
|
mac_addr_is_aligned, vdev_id,
|
|
mod_id);
|
|
qdf_atomic_dec(&link_peer_soc->ref_count);
|
|
return peer;
|
|
}
|
|
|
|
qdf_export_symbol(dp_mlo_link_peer_hash_find_by_chip_id);
|
|
|
|
void dp_mlo_get_rx_hash_key(struct dp_soc *soc,
|
|
struct cdp_lro_hash_config *lro_hash)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *ml_ctxt = be_soc->ml_ctxt;
|
|
|
|
if (!be_soc->mlo_enabled || !ml_ctxt)
|
|
return dp_get_rx_hash_key_bytes(lro_hash);
|
|
|
|
qdf_mem_copy(lro_hash->toeplitz_hash_ipv4, ml_ctxt->toeplitz_hash_ipv4,
|
|
(sizeof(lro_hash->toeplitz_hash_ipv4[0]) *
|
|
LRO_IPV4_SEED_ARR_SZ));
|
|
qdf_mem_copy(lro_hash->toeplitz_hash_ipv6, ml_ctxt->toeplitz_hash_ipv6,
|
|
(sizeof(lro_hash->toeplitz_hash_ipv6[0]) *
|
|
LRO_IPV6_SEED_ARR_SZ));
|
|
}
|
|
|
|
struct dp_soc *
|
|
dp_rx_replenish_soc_get(struct dp_soc *soc, uint8_t chip_id)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *mlo_ctxt = be_soc->ml_ctxt;
|
|
struct dp_soc *replenish_soc;
|
|
|
|
if (!be_soc->mlo_enabled || !mlo_ctxt)
|
|
return soc;
|
|
|
|
if (be_soc->mlo_chip_id == chip_id)
|
|
return soc;
|
|
|
|
replenish_soc = dp_mlo_get_soc_ref_by_chip_id(mlo_ctxt, chip_id);
|
|
if (qdf_unlikely(!replenish_soc)) {
|
|
dp_alert("replenish SOC is NULL");
|
|
qdf_assert_always(0);
|
|
}
|
|
|
|
return replenish_soc;
|
|
}
|
|
|
|
uint8_t dp_soc_get_num_soc_be(struct dp_soc *soc)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *mlo_ctxt = be_soc->ml_ctxt;
|
|
|
|
if (!be_soc->mlo_enabled || !mlo_ctxt)
|
|
return 1;
|
|
|
|
return mlo_ctxt->ml_soc_cnt;
|
|
}
|
|
|
|
struct dp_soc *
|
|
dp_soc_get_by_idle_bm_id(struct dp_soc *soc, uint8_t idle_bm_id)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *mlo_ctxt = be_soc->ml_ctxt;
|
|
struct dp_soc *partner_soc = NULL;
|
|
uint8_t chip_id;
|
|
|
|
if (!be_soc->mlo_enabled || !mlo_ctxt)
|
|
return soc;
|
|
|
|
for (chip_id = 0; chip_id < WLAN_MAX_MLO_CHIPS; chip_id++) {
|
|
partner_soc = dp_mlo_get_soc_ref_by_chip_id(mlo_ctxt, chip_id);
|
|
|
|
if (!partner_soc)
|
|
continue;
|
|
|
|
if (partner_soc->idle_link_bm_id == idle_bm_id)
|
|
return partner_soc;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef WLAN_MLO_MULTI_CHIP
|
|
static void dp_print_mlo_partner_list(struct dp_vdev_be *be_vdev,
|
|
struct dp_vdev *partner_vdev,
|
|
void *arg)
|
|
{
|
|
struct dp_vdev_be *partner_vdev_be = NULL;
|
|
struct dp_soc_be *partner_soc_be = NULL;
|
|
|
|
partner_vdev_be = dp_get_be_vdev_from_dp_vdev(partner_vdev);
|
|
partner_soc_be = dp_get_be_soc_from_dp_soc(partner_vdev->pdev->soc);
|
|
|
|
DP_PRINT_STATS("is_bridge_vap = %s, mcast_primary = %s, vdev_id = %d, pdev_id = %d, chip_id = %d",
|
|
partner_vdev->is_bridge_vdev ? "true" : "false",
|
|
partner_vdev_be->mcast_primary ? "true" : "false",
|
|
partner_vdev->vdev_id,
|
|
partner_vdev->pdev->pdev_id,
|
|
partner_soc_be->mlo_chip_id);
|
|
}
|
|
|
|
void dp_mlo_iter_ptnr_vdev(struct dp_soc_be *be_soc,
|
|
struct dp_vdev_be *be_vdev,
|
|
dp_ptnr_vdev_iter_func func,
|
|
void *arg,
|
|
enum dp_mod_id mod_id,
|
|
uint8_t type,
|
|
bool include_self_vdev)
|
|
{
|
|
int i = 0;
|
|
int j = 0;
|
|
struct dp_mlo_ctxt *dp_mlo = be_soc->ml_ctxt;
|
|
struct dp_vdev *self_vdev = &be_vdev->vdev;
|
|
|
|
if (type < DP_LINK_VDEV_ITER || type > DP_ALL_VDEV_ITER) {
|
|
dp_err("invalid iterate type");
|
|
return;
|
|
}
|
|
|
|
if (!be_vdev->mlo_dev_ctxt) {
|
|
if (!include_self_vdev)
|
|
return;
|
|
(*func)(be_vdev, self_vdev, arg);
|
|
}
|
|
|
|
for (i = 0; (i < WLAN_MAX_MLO_CHIPS) &&
|
|
IS_LINK_VDEV_ITER_REQUIRED(type); i++) {
|
|
struct dp_soc *ptnr_soc =
|
|
dp_mlo_get_soc_ref_by_chip_id(dp_mlo, i);
|
|
|
|
if (!ptnr_soc)
|
|
continue;
|
|
for (j = 0 ; j < WLAN_MAX_MLO_LINKS_PER_SOC ; j++) {
|
|
struct dp_vdev *ptnr_vdev;
|
|
|
|
ptnr_vdev = dp_vdev_get_ref_by_id(
|
|
ptnr_soc,
|
|
be_vdev->mlo_dev_ctxt->vdev_list[i][j],
|
|
mod_id);
|
|
if (!ptnr_vdev)
|
|
continue;
|
|
|
|
if ((ptnr_vdev == self_vdev) && (!include_self_vdev)) {
|
|
dp_vdev_unref_delete(ptnr_vdev->pdev->soc,
|
|
ptnr_vdev,
|
|
mod_id);
|
|
continue;
|
|
}
|
|
|
|
(*func)(be_vdev, ptnr_vdev, arg);
|
|
dp_vdev_unref_delete(ptnr_vdev->pdev->soc,
|
|
ptnr_vdev,
|
|
mod_id);
|
|
}
|
|
}
|
|
|
|
for (i = 0; (i < WLAN_MAX_MLO_CHIPS) &&
|
|
IS_BRIDGE_VDEV_ITER_REQUIRED(type); i++) {
|
|
struct dp_soc *ptnr_soc =
|
|
dp_mlo_get_soc_ref_by_chip_id(dp_mlo, i);
|
|
|
|
if (!ptnr_soc)
|
|
continue;
|
|
for (j = 0 ; j < WLAN_MAX_MLO_LINKS_PER_SOC ; j++) {
|
|
struct dp_vdev *bridge_vdev;
|
|
|
|
bridge_vdev = dp_vdev_get_ref_by_id(
|
|
ptnr_soc,
|
|
be_vdev->mlo_dev_ctxt->bridge_vdev[i][j],
|
|
mod_id);
|
|
|
|
if (!bridge_vdev)
|
|
continue;
|
|
|
|
if ((bridge_vdev == self_vdev) &&
|
|
(!include_self_vdev)) {
|
|
dp_vdev_unref_delete(
|
|
bridge_vdev->pdev->soc,
|
|
bridge_vdev,
|
|
mod_id);
|
|
continue;
|
|
}
|
|
|
|
(*func)(be_vdev, bridge_vdev, arg);
|
|
dp_vdev_unref_delete(bridge_vdev->pdev->soc,
|
|
bridge_vdev,
|
|
mod_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
qdf_export_symbol(dp_mlo_iter_ptnr_vdev);
|
|
|
|
void dp_mlo_debug_print_ptnr_info(struct dp_vdev *vdev)
|
|
{
|
|
struct dp_vdev_be *be_vdev = NULL;
|
|
struct dp_soc_be *be_soc = NULL;
|
|
|
|
be_soc = dp_get_be_soc_from_dp_soc(vdev->pdev->soc);
|
|
be_vdev = dp_get_be_vdev_from_dp_vdev(vdev);
|
|
|
|
DP_PRINT_STATS("self vdev is_bridge_vap = %s, mcast_primary = %s, vdev = %d, pdev_id = %d, chip_id = %d",
|
|
vdev->is_bridge_vdev ? "true" : "false",
|
|
be_vdev->mcast_primary ? "true" : "false",
|
|
vdev->vdev_id,
|
|
vdev->pdev->pdev_id,
|
|
dp_mlo_get_chip_id(vdev->pdev->soc));
|
|
|
|
dp_mlo_iter_ptnr_vdev(be_soc, be_vdev,
|
|
dp_print_mlo_partner_list,
|
|
NULL, DP_MOD_ID_GENERIC_STATS,
|
|
DP_ALL_VDEV_ITER,
|
|
DP_VDEV_ITERATE_SKIP_SELF);
|
|
}
|
|
#endif
|
|
|
|
#ifdef WLAN_MCAST_MLO
|
|
struct dp_vdev *dp_mlo_get_mcast_primary_vdev(struct dp_soc_be *be_soc,
|
|
struct dp_vdev_be *be_vdev,
|
|
enum dp_mod_id mod_id)
|
|
{
|
|
int i = 0;
|
|
int j = 0;
|
|
struct dp_mlo_ctxt *dp_mlo = be_soc->ml_ctxt;
|
|
struct dp_vdev *vdev = (struct dp_vdev *)be_vdev;
|
|
|
|
if (!be_vdev->mlo_dev_ctxt) {
|
|
return NULL;
|
|
}
|
|
|
|
if (be_vdev->mcast_primary) {
|
|
if (dp_vdev_get_ref((struct dp_soc *)be_soc, vdev, mod_id) !=
|
|
QDF_STATUS_SUCCESS)
|
|
return NULL;
|
|
|
|
return vdev;
|
|
}
|
|
|
|
for (i = 0; i < WLAN_MAX_MLO_CHIPS ; i++) {
|
|
struct dp_soc *ptnr_soc =
|
|
dp_mlo_get_soc_ref_by_chip_id(dp_mlo, i);
|
|
|
|
if (!ptnr_soc)
|
|
continue;
|
|
for (j = 0 ; j < WLAN_MAX_MLO_LINKS_PER_SOC ; j++) {
|
|
struct dp_vdev *ptnr_vdev = NULL;
|
|
struct dp_vdev_be *be_ptnr_vdev = NULL;
|
|
|
|
ptnr_vdev = dp_vdev_get_ref_by_id(
|
|
ptnr_soc,
|
|
be_vdev->mlo_dev_ctxt->vdev_list[i][j],
|
|
mod_id);
|
|
if (!ptnr_vdev)
|
|
continue;
|
|
be_ptnr_vdev = dp_get_be_vdev_from_dp_vdev(ptnr_vdev);
|
|
if (be_ptnr_vdev->mcast_primary)
|
|
return ptnr_vdev;
|
|
dp_vdev_unref_delete(be_ptnr_vdev->vdev.pdev->soc,
|
|
&be_ptnr_vdev->vdev,
|
|
mod_id);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
qdf_export_symbol(dp_mlo_get_mcast_primary_vdev);
|
|
#endif
|
|
|
|
/**
|
|
* dp_mlo_iter_ptnr_soc() - iterate through mlo soc list and call the callback
|
|
* @be_soc: dp_soc_be pointer
|
|
* @func: Function to be called for each soc
|
|
* @arg: context to be passed to the callback
|
|
*
|
|
* Return: true if mlo is enabled, false if mlo is disabled
|
|
*/
|
|
bool dp_mlo_iter_ptnr_soc(struct dp_soc_be *be_soc, dp_ptnr_soc_iter_func func,
|
|
void *arg)
|
|
{
|
|
int i = 0;
|
|
struct dp_mlo_ctxt *dp_mlo = be_soc->ml_ctxt;
|
|
|
|
if (!be_soc->mlo_enabled || !be_soc->ml_ctxt)
|
|
return false;
|
|
|
|
for (i = 0; i < WLAN_MAX_MLO_CHIPS ; i++) {
|
|
struct dp_soc *ptnr_soc =
|
|
dp_mlo_get_soc_ref_by_chip_id(dp_mlo, i);
|
|
|
|
if (!ptnr_soc)
|
|
continue;
|
|
(*func)(ptnr_soc, arg, i);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
qdf_export_symbol(dp_mlo_iter_ptnr_soc);
|
|
|
|
static inline uint64_t dp_mlo_get_mlo_ts_offset(struct dp_pdev_be *be_pdev)
|
|
{
|
|
struct dp_soc *soc;
|
|
struct dp_pdev *pdev;
|
|
struct dp_soc_be *be_soc;
|
|
uint32_t mlo_offset;
|
|
|
|
pdev = &be_pdev->pdev;
|
|
soc = pdev->soc;
|
|
be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
|
|
mlo_offset = be_soc->mlo_tstamp_offset;
|
|
|
|
return mlo_offset;
|
|
}
|
|
|
|
int32_t dp_mlo_get_delta_tsf2_wrt_mlo_offset(struct dp_soc *soc,
|
|
uint8_t hw_link_id)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *ml_ctxt = be_soc->ml_ctxt;
|
|
struct dp_pdev_be *be_pdev;
|
|
int32_t delta_tsf2_mlo_offset;
|
|
int32_t mlo_offset, delta_tsf2;
|
|
|
|
if (!ml_ctxt)
|
|
return 0;
|
|
|
|
be_pdev = dp_mlo_get_be_pdev_from_link_id(ml_ctxt, hw_link_id);
|
|
if (!be_pdev)
|
|
return 0;
|
|
|
|
mlo_offset = dp_mlo_get_mlo_ts_offset(be_pdev);
|
|
delta_tsf2 = be_pdev->delta_tsf2;
|
|
|
|
delta_tsf2_mlo_offset = mlo_offset - delta_tsf2;
|
|
|
|
return delta_tsf2_mlo_offset;
|
|
}
|
|
|
|
int32_t dp_mlo_get_delta_tqm_wrt_mlo_offset(struct dp_soc *soc)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
int32_t delta_tqm_mlo_offset;
|
|
int32_t mlo_offset, delta_tqm;
|
|
|
|
mlo_offset = be_soc->mlo_tstamp_offset;
|
|
delta_tqm = be_soc->delta_tqm;
|
|
|
|
delta_tqm_mlo_offset = mlo_offset - delta_tqm;
|
|
|
|
return delta_tqm_mlo_offset;
|
|
}
|
|
|
|
#ifdef DP_UMAC_HW_RESET_SUPPORT
|
|
/**
|
|
* dp_umac_reset_update_partner_map() - Update Umac reset partner map
|
|
* @mlo_ctx: DP ML context handle
|
|
* @chip_id: chip id
|
|
* @set: flag indicating whether to set or clear the bit
|
|
*
|
|
* Return: void
|
|
*/
|
|
static void dp_umac_reset_update_partner_map(struct dp_mlo_ctxt *mlo_ctx,
|
|
int chip_id, bool set)
|
|
{
|
|
struct dp_soc_mlo_umac_reset_ctx *grp_umac_reset_ctx =
|
|
&mlo_ctx->grp_umac_reset_ctx;
|
|
|
|
if (set)
|
|
qdf_atomic_set_bit(chip_id, &grp_umac_reset_ctx->partner_map);
|
|
else
|
|
qdf_atomic_clear_bit(chip_id, &grp_umac_reset_ctx->partner_map);
|
|
}
|
|
|
|
QDF_STATUS dp_umac_reset_notify_asserted_soc(struct dp_soc *soc)
|
|
{
|
|
struct dp_mlo_ctxt *mlo_ctx;
|
|
struct dp_soc_be *be_soc;
|
|
|
|
be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
if (!be_soc) {
|
|
dp_umac_reset_err("null be_soc");
|
|
return QDF_STATUS_E_NULL_VALUE;
|
|
}
|
|
|
|
mlo_ctx = be_soc->ml_ctxt;
|
|
if (!mlo_ctx) {
|
|
/* This API can be called for non-MLO SOC as well. Hence, return
|
|
* the status as success when mlo_ctx is NULL.
|
|
*/
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
|
|
dp_umac_reset_update_partner_map(mlo_ctx, be_soc->mlo_chip_id, false);
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* dp_umac_reset_complete_umac_recovery() - Complete Umac reset session
|
|
* @soc: dp soc handle
|
|
*
|
|
* Return: void
|
|
*/
|
|
void dp_umac_reset_complete_umac_recovery(struct dp_soc *soc)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *mlo_ctx = be_soc->ml_ctxt;
|
|
struct dp_soc_mlo_umac_reset_ctx *grp_umac_reset_ctx;
|
|
|
|
if (!mlo_ctx) {
|
|
dp_umac_reset_alert("Umac reset was handled on soc %pK", soc);
|
|
return;
|
|
}
|
|
|
|
grp_umac_reset_ctx = &mlo_ctx->grp_umac_reset_ctx;
|
|
qdf_spin_lock_bh(&grp_umac_reset_ctx->grp_ctx_lock);
|
|
|
|
grp_umac_reset_ctx->umac_reset_in_progress = false;
|
|
grp_umac_reset_ctx->is_target_recovery = false;
|
|
grp_umac_reset_ctx->response_map = 0;
|
|
grp_umac_reset_ctx->request_map = 0;
|
|
grp_umac_reset_ctx->initiator_chip_id = 0;
|
|
|
|
qdf_spin_unlock_bh(&grp_umac_reset_ctx->grp_ctx_lock);
|
|
|
|
dp_umac_reset_alert("Umac reset was handled on mlo group ctxt %pK",
|
|
mlo_ctx);
|
|
}
|
|
|
|
/**
|
|
* dp_umac_reset_initiate_umac_recovery() - Initiate Umac reset session
|
|
* @soc: dp soc handle
|
|
* @umac_reset_ctx: Umac reset context
|
|
* @rx_event: Rx event received
|
|
* @is_target_recovery: Flag to indicate if it is triggered for target recovery
|
|
*
|
|
* Return: status
|
|
*/
|
|
QDF_STATUS dp_umac_reset_initiate_umac_recovery(struct dp_soc *soc,
|
|
struct dp_soc_umac_reset_ctx *umac_reset_ctx,
|
|
enum umac_reset_rx_event rx_event,
|
|
bool is_target_recovery)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *mlo_ctx = be_soc->ml_ctxt;
|
|
struct dp_soc_mlo_umac_reset_ctx *grp_umac_reset_ctx;
|
|
QDF_STATUS status = QDF_STATUS_SUCCESS;
|
|
|
|
if (!mlo_ctx)
|
|
return dp_umac_reset_validate_n_update_state_machine_on_rx(
|
|
umac_reset_ctx, rx_event,
|
|
UMAC_RESET_STATE_WAIT_FOR_TRIGGER,
|
|
UMAC_RESET_STATE_DO_TRIGGER_RECEIVED);
|
|
|
|
grp_umac_reset_ctx = &mlo_ctx->grp_umac_reset_ctx;
|
|
qdf_spin_lock_bh(&grp_umac_reset_ctx->grp_ctx_lock);
|
|
|
|
if (grp_umac_reset_ctx->umac_reset_in_progress) {
|
|
qdf_spin_unlock_bh(&grp_umac_reset_ctx->grp_ctx_lock);
|
|
return QDF_STATUS_E_INVAL;
|
|
}
|
|
|
|
status = dp_umac_reset_validate_n_update_state_machine_on_rx(
|
|
umac_reset_ctx, rx_event,
|
|
UMAC_RESET_STATE_WAIT_FOR_TRIGGER,
|
|
UMAC_RESET_STATE_DO_TRIGGER_RECEIVED);
|
|
|
|
if (status != QDF_STATUS_SUCCESS) {
|
|
qdf_spin_unlock_bh(&grp_umac_reset_ctx->grp_ctx_lock);
|
|
return status;
|
|
}
|
|
|
|
grp_umac_reset_ctx->umac_reset_in_progress = true;
|
|
grp_umac_reset_ctx->is_target_recovery = is_target_recovery;
|
|
|
|
/* We don't wait for the 'Umac trigger' message from all socs */
|
|
grp_umac_reset_ctx->request_map = grp_umac_reset_ctx->partner_map;
|
|
grp_umac_reset_ctx->response_map = grp_umac_reset_ctx->partner_map;
|
|
grp_umac_reset_ctx->initiator_chip_id = dp_mlo_get_chip_id(soc);
|
|
grp_umac_reset_ctx->umac_reset_count++;
|
|
|
|
qdf_spin_unlock_bh(&grp_umac_reset_ctx->grp_ctx_lock);
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* dp_umac_reset_handle_action_cb() - Function to call action callback
|
|
* @soc: dp soc handle
|
|
* @umac_reset_ctx: Umac reset context
|
|
* @action: Action to call the callback for
|
|
*
|
|
* Return: QDF_STATUS status
|
|
*/
|
|
QDF_STATUS
|
|
dp_umac_reset_handle_action_cb(struct dp_soc *soc,
|
|
struct dp_soc_umac_reset_ctx *umac_reset_ctx,
|
|
enum umac_reset_action action)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *mlo_ctx = be_soc->ml_ctxt;
|
|
struct dp_soc_mlo_umac_reset_ctx *grp_umac_reset_ctx;
|
|
|
|
if (!mlo_ctx) {
|
|
dp_umac_reset_debug("MLO context is Null");
|
|
goto handle;
|
|
}
|
|
|
|
grp_umac_reset_ctx = &mlo_ctx->grp_umac_reset_ctx;
|
|
qdf_spin_lock_bh(&grp_umac_reset_ctx->grp_ctx_lock);
|
|
|
|
qdf_atomic_set_bit(dp_mlo_get_chip_id(soc),
|
|
&grp_umac_reset_ctx->request_map);
|
|
|
|
dp_umac_reset_debug("partner_map %u request_map %u",
|
|
grp_umac_reset_ctx->partner_map,
|
|
grp_umac_reset_ctx->request_map);
|
|
|
|
/* This logic is needed for synchronization between mlo socs */
|
|
if ((grp_umac_reset_ctx->partner_map & grp_umac_reset_ctx->request_map)
|
|
!= grp_umac_reset_ctx->partner_map) {
|
|
struct hif_softc *hif_sc = HIF_GET_SOFTC(soc->hif_handle);
|
|
struct hif_umac_reset_ctx *hif_umac_reset_ctx;
|
|
|
|
if (!hif_sc) {
|
|
hif_err("scn is null");
|
|
qdf_assert_always(0);
|
|
return QDF_STATUS_E_FAILURE;
|
|
}
|
|
|
|
hif_umac_reset_ctx = &hif_sc->umac_reset_ctx;
|
|
|
|
/* Mark the action as pending */
|
|
umac_reset_ctx->pending_action = action;
|
|
/* Reschedule the tasklet and exit */
|
|
tasklet_hi_schedule(&hif_umac_reset_ctx->intr_tq);
|
|
qdf_spin_unlock_bh(&grp_umac_reset_ctx->grp_ctx_lock);
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
|
|
qdf_spin_unlock_bh(&grp_umac_reset_ctx->grp_ctx_lock);
|
|
umac_reset_ctx->pending_action = UMAC_RESET_ACTION_NONE;
|
|
|
|
handle:
|
|
if (!umac_reset_ctx->rx_actions.cb[action]) {
|
|
dp_umac_reset_err("rx callback is NULL");
|
|
return QDF_STATUS_E_FAILURE;
|
|
}
|
|
|
|
return umac_reset_ctx->rx_actions.cb[action](soc);
|
|
}
|
|
|
|
/**
|
|
* dp_umac_reset_post_tx_cmd() - Iterate partner socs and post Tx command
|
|
* @umac_reset_ctx: UMAC reset context
|
|
* @tx_cmd: Tx command to be posted
|
|
*
|
|
* Return: QDF status of operation
|
|
*/
|
|
QDF_STATUS
|
|
dp_umac_reset_post_tx_cmd(struct dp_soc_umac_reset_ctx *umac_reset_ctx,
|
|
enum umac_reset_tx_cmd tx_cmd)
|
|
{
|
|
struct dp_soc *soc = container_of(umac_reset_ctx, struct dp_soc,
|
|
umac_reset_ctx);
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *mlo_ctx = be_soc->ml_ctxt;
|
|
struct dp_soc_mlo_umac_reset_ctx *grp_umac_reset_ctx;
|
|
|
|
if (!mlo_ctx) {
|
|
dp_umac_reset_post_tx_cmd_via_shmem(soc, &tx_cmd, 0);
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
|
|
grp_umac_reset_ctx = &mlo_ctx->grp_umac_reset_ctx;
|
|
qdf_spin_lock_bh(&grp_umac_reset_ctx->grp_ctx_lock);
|
|
|
|
qdf_atomic_set_bit(dp_mlo_get_chip_id(soc),
|
|
&grp_umac_reset_ctx->response_map);
|
|
|
|
/* This logic is needed for synchronization between mlo socs */
|
|
if ((grp_umac_reset_ctx->partner_map & grp_umac_reset_ctx->response_map)
|
|
!= grp_umac_reset_ctx->partner_map) {
|
|
dp_umac_reset_debug(
|
|
"Response(s) pending : expected map %u current map %u",
|
|
grp_umac_reset_ctx->partner_map,
|
|
grp_umac_reset_ctx->response_map);
|
|
|
|
qdf_spin_unlock_bh(&grp_umac_reset_ctx->grp_ctx_lock);
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
|
|
dp_umac_reset_debug(
|
|
"All responses received: expected map %u current map %u",
|
|
grp_umac_reset_ctx->partner_map,
|
|
grp_umac_reset_ctx->response_map);
|
|
|
|
grp_umac_reset_ctx->response_map = 0;
|
|
grp_umac_reset_ctx->request_map = 0;
|
|
qdf_spin_unlock_bh(&grp_umac_reset_ctx->grp_ctx_lock);
|
|
|
|
dp_mlo_iter_ptnr_soc(be_soc, &dp_umac_reset_post_tx_cmd_via_shmem,
|
|
&tx_cmd);
|
|
|
|
if (tx_cmd == UMAC_RESET_TX_CMD_POST_RESET_COMPLETE_DONE)
|
|
dp_umac_reset_complete_umac_recovery(soc);
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* dp_umac_reset_initiator_check() - Check if soc is the Umac reset initiator
|
|
* @soc: dp soc handle
|
|
*
|
|
* Return: true if the soc is initiator or false otherwise
|
|
*/
|
|
bool dp_umac_reset_initiator_check(struct dp_soc *soc)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *mlo_ctx = be_soc->ml_ctxt;
|
|
|
|
if (!mlo_ctx)
|
|
return true;
|
|
|
|
return (mlo_ctx->grp_umac_reset_ctx.initiator_chip_id ==
|
|
dp_mlo_get_chip_id(soc));
|
|
}
|
|
|
|
/**
|
|
* dp_umac_reset_target_recovery_check() - Check if this is for target recovery
|
|
* @soc: dp soc handle
|
|
*
|
|
* Return: true if the session is for target recovery or false otherwise
|
|
*/
|
|
bool dp_umac_reset_target_recovery_check(struct dp_soc *soc)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *mlo_ctx = be_soc->ml_ctxt;
|
|
|
|
if (!mlo_ctx)
|
|
return false;
|
|
|
|
return mlo_ctx->grp_umac_reset_ctx.is_target_recovery;
|
|
}
|
|
|
|
/**
|
|
* dp_umac_reset_is_soc_ignored() - Check if this soc is to be ignored
|
|
* @soc: dp soc handle
|
|
*
|
|
* Return: true if the soc is ignored or false otherwise
|
|
*/
|
|
bool dp_umac_reset_is_soc_ignored(struct dp_soc *soc)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *mlo_ctx = be_soc->ml_ctxt;
|
|
|
|
if (!mlo_ctx)
|
|
return false;
|
|
|
|
return !qdf_atomic_test_bit(dp_mlo_get_chip_id(soc),
|
|
&mlo_ctx->grp_umac_reset_ctx.partner_map);
|
|
}
|
|
|
|
QDF_STATUS dp_mlo_umac_reset_stats_print(struct dp_soc *soc)
|
|
{
|
|
struct dp_mlo_ctxt *mlo_ctx;
|
|
struct dp_soc_be *be_soc;
|
|
struct dp_soc_mlo_umac_reset_ctx *grp_umac_reset_ctx;
|
|
|
|
be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
if (!be_soc) {
|
|
dp_umac_reset_err("null be_soc");
|
|
return QDF_STATUS_E_NULL_VALUE;
|
|
}
|
|
|
|
mlo_ctx = be_soc->ml_ctxt;
|
|
if (!mlo_ctx) {
|
|
/* This API can be called for non-MLO SOC as well. Hence, return
|
|
* the status as success when mlo_ctx is NULL.
|
|
*/
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
|
|
grp_umac_reset_ctx = &mlo_ctx->grp_umac_reset_ctx;
|
|
|
|
DP_UMAC_RESET_PRINT_STATS("MLO UMAC RESET stats\n"
|
|
"\t\tPartner map :%x\n"
|
|
"\t\tRequest map :%x\n"
|
|
"\t\tResponse map :%x\n"
|
|
"\t\tIs target recovery :%d\n"
|
|
"\t\tIs Umac reset inprogress :%d\n"
|
|
"\t\tNumber of UMAC reset triggered:%d\n"
|
|
"\t\tInitiator chip ID :%d\n",
|
|
grp_umac_reset_ctx->partner_map,
|
|
grp_umac_reset_ctx->request_map,
|
|
grp_umac_reset_ctx->response_map,
|
|
grp_umac_reset_ctx->is_target_recovery,
|
|
grp_umac_reset_ctx->umac_reset_in_progress,
|
|
grp_umac_reset_ctx->umac_reset_count,
|
|
grp_umac_reset_ctx->initiator_chip_id);
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
|
|
enum cdp_umac_reset_state
|
|
dp_get_umac_reset_in_progress_state(struct cdp_soc_t *psoc)
|
|
{
|
|
struct dp_soc_umac_reset_ctx *umac_reset_ctx;
|
|
struct dp_soc *soc = (struct dp_soc *)psoc;
|
|
struct dp_soc_mlo_umac_reset_ctx *grp_umac_reset_ctx;
|
|
struct dp_soc_be *be_soc = NULL;
|
|
struct dp_mlo_ctxt *mlo_ctx = NULL;
|
|
enum cdp_umac_reset_state umac_reset_is_inprogress;
|
|
|
|
if (!soc) {
|
|
dp_umac_reset_err("DP SOC is null");
|
|
return CDP_UMAC_RESET_INVALID_STATE;
|
|
}
|
|
|
|
umac_reset_ctx = &soc->umac_reset_ctx;
|
|
|
|
be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
if (be_soc)
|
|
mlo_ctx = be_soc->ml_ctxt;
|
|
|
|
if (mlo_ctx) {
|
|
grp_umac_reset_ctx = &mlo_ctx->grp_umac_reset_ctx;
|
|
umac_reset_is_inprogress =
|
|
grp_umac_reset_ctx->umac_reset_in_progress;
|
|
} else {
|
|
umac_reset_is_inprogress = (umac_reset_ctx->current_state !=
|
|
UMAC_RESET_STATE_WAIT_FOR_TRIGGER);
|
|
}
|
|
|
|
if (umac_reset_is_inprogress)
|
|
return CDP_UMAC_RESET_IN_PROGRESS;
|
|
|
|
/* Check if the umac reset was in progress during the buffer
|
|
* window.
|
|
*/
|
|
umac_reset_is_inprogress =
|
|
((qdf_get_log_timestamp_usecs() -
|
|
umac_reset_ctx->ts.post_reset_complete_done) <=
|
|
(wlan_cfg_get_umac_reset_buffer_window_ms(soc->wlan_cfg_ctx) *
|
|
1000));
|
|
|
|
return (umac_reset_is_inprogress ?
|
|
CDP_UMAC_RESET_IN_PROGRESS_DURING_BUFFER_WINDOW :
|
|
CDP_UMAC_RESET_NOT_IN_PROGRESS);
|
|
}
|
|
|
|
/**
|
|
* dp_get_global_tx_desc_cleanup_flag() - Get cleanup needed flag
|
|
* @soc: dp soc handle
|
|
*
|
|
* Return: cleanup needed/ not needed
|
|
*/
|
|
bool dp_get_global_tx_desc_cleanup_flag(struct dp_soc *soc)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *mlo_ctx = be_soc->ml_ctxt;
|
|
struct dp_soc_mlo_umac_reset_ctx *grp_umac_reset_ctx;
|
|
bool flag;
|
|
|
|
if (!mlo_ctx)
|
|
return true;
|
|
|
|
grp_umac_reset_ctx = &mlo_ctx->grp_umac_reset_ctx;
|
|
qdf_spin_lock_bh(&grp_umac_reset_ctx->grp_ctx_lock);
|
|
|
|
flag = grp_umac_reset_ctx->tx_desc_pool_cleaned;
|
|
if (!flag)
|
|
grp_umac_reset_ctx->tx_desc_pool_cleaned = true;
|
|
|
|
qdf_spin_unlock_bh(&grp_umac_reset_ctx->grp_ctx_lock);
|
|
|
|
return !flag;
|
|
}
|
|
|
|
/**
|
|
* dp_reset_global_tx_desc_cleanup_flag() - Reset cleanup needed flag
|
|
* @soc: dp soc handle
|
|
*
|
|
* Return: None
|
|
*/
|
|
void dp_reset_global_tx_desc_cleanup_flag(struct dp_soc *soc)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *mlo_ctx = be_soc->ml_ctxt;
|
|
struct dp_soc_mlo_umac_reset_ctx *grp_umac_reset_ctx;
|
|
|
|
if (!mlo_ctx)
|
|
return;
|
|
|
|
grp_umac_reset_ctx = &mlo_ctx->grp_umac_reset_ctx;
|
|
grp_umac_reset_ctx->tx_desc_pool_cleaned = false;
|
|
}
|
|
#endif
|
|
|
|
struct dp_soc *
|
|
dp_get_soc_by_chip_id_be(struct dp_soc *soc, uint8_t chip_id)
|
|
{
|
|
struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
|
|
struct dp_mlo_ctxt *mlo_ctxt = be_soc->ml_ctxt;
|
|
struct dp_soc *partner_soc;
|
|
|
|
if (!be_soc->mlo_enabled || !mlo_ctxt)
|
|
return soc;
|
|
|
|
if (be_soc->mlo_chip_id == chip_id)
|
|
return soc;
|
|
|
|
partner_soc = dp_mlo_get_soc_ref_by_chip_id(mlo_ctxt, chip_id);
|
|
return partner_soc;
|
|
}
|