blob: 17d4f44a310cc6e4f30ae6b15f76e7756dc4f9b1 [file] [log] [blame]
/*
* Copyright (c) 2016-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.
*/
#include <qdf_types.h>
#include <qdf_lock.h>
#include <qdf_net_types.h>
#include <qdf_lro.h>
#include <qdf_module.h>
#include <hal_hw_headers.h>
#include <hal_api.h>
#include <hif.h>
#include <htt.h>
#include <wdi_event.h>
#include <queue.h>
#include "dp_types.h"
#include "dp_internal.h"
#include "dp_tx.h"
#include "dp_tx_desc.h"
#include "dp_rx.h"
#include "dp_rx_mon.h"
#ifdef DP_RATETABLE_SUPPORT
#include "dp_ratetable.h"
#endif
#include <cdp_txrx_handle.h>
#include <wlan_cfg.h>
#include "cdp_txrx_cmn_struct.h"
#include "cdp_txrx_stats_struct.h"
#include "cdp_txrx_cmn_reg.h"
#include <qdf_util.h>
#include "dp_peer.h"
#include "dp_rx_mon.h"
#include "htt_stats.h"
#include "htt_ppdu_stats.h"
#include "dp_htt.h"
#include "qdf_mem.h" /* qdf_mem_malloc,free */
#include "cfg_ucfg_api.h"
#ifdef QCA_LL_TX_FLOW_CONTROL_V2
#include "cdp_txrx_flow_ctrl_v2.h"
#else
static inline void
cdp_dump_flow_pool_info(struct cdp_soc_t *soc)
{
return;
}
#endif
#include "dp_ipa.h"
#include "dp_cal_client_api.h"
#ifdef FEATURE_WDS
#include "dp_txrx_wds.h"
#endif
#ifdef CONFIG_MCL
#ifndef REMOVE_PKT_LOG
#include <pktlog_ac_api.h>
#include <pktlog_ac.h>
#endif
#endif
#ifdef WLAN_FEATURE_STATS_EXT
#define INIT_RX_HW_STATS_LOCK(_soc) \
qdf_spinlock_create(&(_soc)->rx_hw_stats_lock)
#define DEINIT_RX_HW_STATS_LOCK(_soc) \
qdf_spinlock_destroy(&(_soc)->rx_hw_stats_lock)
#else
#define INIT_RX_HW_STATS_LOCK(_soc) /* no op */
#define DEINIT_RX_HW_STATS_LOCK(_soc) /* no op */
#endif
#ifdef DP_PEER_EXTENDED_API
#define SET_PEER_REF_CNT_ONE(_peer) \
qdf_atomic_set(&(_peer)->ref_cnt, 1)
#else
#define SET_PEER_REF_CNT_ONE(_peer)
#endif
#ifdef WLAN_FEATURE_DP_EVENT_HISTORY
/*
* If WLAN_CFG_INT_NUM_CONTEXTS is changed, HIF_NUM_INT_CONTEXTS
* also should be updated accordingly
*/
QDF_COMPILE_TIME_ASSERT(num_intr_grps,
HIF_NUM_INT_CONTEXTS == WLAN_CFG_INT_NUM_CONTEXTS);
/*
* HIF_EVENT_HIST_MAX should always be power of 2
*/
QDF_COMPILE_TIME_ASSERT(hif_event_history_size,
(HIF_EVENT_HIST_MAX & (HIF_EVENT_HIST_MAX - 1)) == 0);
#endif /* WLAN_FEATURE_DP_EVENT_HISTORY */
#ifdef WLAN_RX_PKT_CAPTURE_ENH
#include "dp_rx_mon_feature.h"
#else
/*
* dp_config_enh_rx_capture()- API to enable/disable enhanced rx capture
* @pdev_handle: DP_PDEV handle
* @val: user provided value
*
* Return: QDF_STATUS
*/
static QDF_STATUS
dp_config_enh_rx_capture(struct cdp_pdev *pdev_handle, uint8_t val)
{
return QDF_STATUS_E_INVAL;
}
#endif /* WLAN_RX_PKT_CAPTURE_ENH */
#ifdef WLAN_TX_PKT_CAPTURE_ENH
#include "dp_tx_capture.h"
#else
/*
* dp_config_enh_tx_capture()- API to enable/disable enhanced tx capture
* @pdev_handle: DP_PDEV handle
* @val: user provided value
*
* Return: QDF_STATUS
*/
static QDF_STATUS
dp_config_enh_tx_capture(struct cdp_pdev *pdev_handle, int val)
{
return QDF_STATUS_E_INVAL;
}
#endif
void *dp_soc_init(void *dpsoc, HTC_HANDLE htc_handle, void *hif_handle);
static void dp_pdev_detach(struct cdp_pdev *txrx_pdev, int force);
static struct dp_soc *
dp_soc_attach(void *ctrl_psoc, HTC_HANDLE htc_handle, qdf_device_t qdf_osdev,
struct ol_if_ops *ol_ops, uint16_t device_id);
static void dp_pktlogmod_exit(struct dp_pdev *handle);
static void *dp_peer_create_wifi3(struct cdp_vdev *vdev_handle,
uint8_t *peer_mac_addr,
struct cdp_ctrl_objmgr_peer *ctrl_peer);
static void dp_peer_delete_wifi3(void *peer_handle, uint32_t bitmap);
static void dp_ppdu_ring_reset(struct dp_pdev *pdev);
static void dp_ppdu_ring_cfg(struct dp_pdev *pdev);
static void dp_vdev_flush_peers(struct cdp_vdev *vdev_handle,
bool unmap_only);
#ifdef ENABLE_VERBOSE_DEBUG
bool is_dp_verbose_debug_enabled;
#endif
static uint8_t dp_soc_ring_if_nss_offloaded(struct dp_soc *soc,
enum hal_ring_type ring_type,
int ring_num);
#define DP_INTR_POLL_TIMER_MS 5
/* Generic AST entry aging timer value */
#define DP_AST_AGING_TIMER_DEFAULT_MS 1000
#define DP_MCS_LENGTH (6*MAX_MCS)
#define DP_CURR_FW_STATS_AVAIL 19
#define DP_HTT_DBG_EXT_STATS_MAX 256
#define DP_MAX_SLEEP_TIME 100
#ifndef QCA_WIFI_3_0_EMU
#define SUSPEND_DRAIN_WAIT 500
#else
#define SUSPEND_DRAIN_WAIT 3000
#endif
#ifdef IPA_OFFLOAD
/* Exclude IPA rings from the interrupt context */
#define TX_RING_MASK_VAL 0xb
#define RX_RING_MASK_VAL 0x7
#else
#define TX_RING_MASK_VAL 0xF
#define RX_RING_MASK_VAL 0xF
#endif
#define STR_MAXLEN 64
#define RNG_ERR "SRNG setup failed for"
/* Threshold for peer's cached buf queue beyond which frames are dropped */
#define DP_RX_CACHED_BUFQ_THRESH 64
/* Budget to reap monitor status ring */
#define DP_MON_REAP_BUDGET 1024
/**
* default_dscp_tid_map - Default DSCP-TID mapping
*
* DSCP TID
* 000000 0
* 001000 1
* 010000 2
* 011000 3
* 100000 4
* 101000 5
* 110000 6
* 111000 7
*/
static uint8_t default_dscp_tid_map[DSCP_TID_MAP_MAX] = {
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7,
};
/**
* default_pcp_tid_map - Default PCP-TID mapping
*
* PCP TID
* 000 0
* 001 1
* 010 2
* 011 3
* 100 4
* 101 5
* 110 6
* 111 7
*/
static uint8_t default_pcp_tid_map[PCP_TID_MAP_MAX] = {
0, 1, 2, 3, 4, 5, 6, 7,
};
/**
* @brief Cpu to tx ring map
*/
#ifdef CONFIG_WIN
#ifdef WLAN_TX_PKT_CAPTURE_ENH
uint8_t
dp_cpu_ring_map[DP_NSS_CPU_RING_MAP_MAX][WLAN_CFG_INT_NUM_CONTEXTS] = {
{0x0, 0x1, 0x2, 0x0, 0x0, 0x1, 0x2, 0x0, 0x0, 0x1, 0x2},
{0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1},
{0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0},
{0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2},
{0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3},
{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}
};
#else
static uint8_t
dp_cpu_ring_map[DP_NSS_CPU_RING_MAP_MAX][WLAN_CFG_INT_NUM_CONTEXTS] = {
{0x0, 0x1, 0x2, 0x0, 0x0, 0x1, 0x2, 0x0, 0x0, 0x1, 0x2},
{0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1},
{0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0},
{0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2},
{0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3}
};
#endif
#else
static uint8_t
dp_cpu_ring_map[DP_NSS_CPU_RING_MAP_MAX][WLAN_CFG_INT_NUM_CONTEXTS] = {
{0x0, 0x1, 0x2, 0x0, 0x0, 0x1, 0x2},
{0x1, 0x2, 0x1, 0x2, 0x1, 0x2, 0x1},
{0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0},
{0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2},
{0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3}
};
#endif
/**
* @brief Select the type of statistics
*/
enum dp_stats_type {
STATS_FW = 0,
STATS_HOST = 1,
STATS_TYPE_MAX = 2,
};
/**
* @brief General Firmware statistics options
*
*/
enum dp_fw_stats {
TXRX_FW_STATS_INVALID = -1,
};
/**
* dp_stats_mapping_table - Firmware and Host statistics
* currently supported
*/
const int dp_stats_mapping_table[][STATS_TYPE_MAX] = {
{HTT_DBG_EXT_STATS_RESET, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_PDEV_TX, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_PDEV_RX, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_PDEV_TX_HWQ, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_PDEV_TX_SCHED, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_PDEV_ERROR, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_PDEV_TQM, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_TQM_CMDQ, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_TX_DE_INFO, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_PDEV_TX_RATE, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_PDEV_RX_RATE, TXRX_HOST_STATS_INVALID},
{TXRX_FW_STATS_INVALID, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_TX_SELFGEN_INFO, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_TX_MU_HWQ, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_RING_IF_INFO, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_SRNG_INFO, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_SFM_INFO, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_PDEV_TX_MU, TXRX_HOST_STATS_INVALID},
{HTT_DBG_EXT_STATS_ACTIVE_PEERS_LIST, TXRX_HOST_STATS_INVALID},
/* Last ENUM for HTT FW STATS */
{DP_HTT_DBG_EXT_STATS_MAX, TXRX_HOST_STATS_INVALID},
{TXRX_FW_STATS_INVALID, TXRX_CLEAR_STATS},
{TXRX_FW_STATS_INVALID, TXRX_RX_RATE_STATS},
{TXRX_FW_STATS_INVALID, TXRX_TX_RATE_STATS},
{TXRX_FW_STATS_INVALID, TXRX_TX_HOST_STATS},
{TXRX_FW_STATS_INVALID, TXRX_RX_HOST_STATS},
{TXRX_FW_STATS_INVALID, TXRX_AST_STATS},
{TXRX_FW_STATS_INVALID, TXRX_SRNG_PTR_STATS},
{TXRX_FW_STATS_INVALID, TXRX_RX_MON_STATS},
{TXRX_FW_STATS_INVALID, TXRX_REO_QUEUE_STATS},
{TXRX_FW_STATS_INVALID, TXRX_SOC_CFG_PARAMS},
{TXRX_FW_STATS_INVALID, TXRX_PDEV_CFG_PARAMS},
{TXRX_FW_STATS_INVALID, TXRX_SOC_INTERRUPT_STATS},
{TXRX_FW_STATS_INVALID, TXRX_HAL_REG_WRITE_STATS},
};
/* MCL specific functions */
#ifdef CONFIG_MCL
/**
* dp_soc_get_mon_mask_for_interrupt_mode() - get mon mode mask for intr mode
* @soc: pointer to dp_soc handle
* @intr_ctx_num: interrupt context number for which mon mask is needed
*
* For MCL, monitor mode rings are being processed in timer contexts (polled).
* This function is returning 0, since in interrupt mode(softirq based RX),
* we donot want to process monitor mode rings in a softirq.
*
* So, in case packet log is enabled for SAP/STA/P2P modes,
* regular interrupt processing will not process monitor mode rings. It would be
* done in a separate timer context.
*
* Return: 0
*/
static inline
uint32_t dp_soc_get_mon_mask_for_interrupt_mode(struct dp_soc *soc, int intr_ctx_num)
{
return 0;
}
/*
* dp_service_mon_rings()- service monitor rings
* @soc: soc dp handle
* @quota: number of ring entry that can be serviced
*
* Return: None
*
*/
static void dp_service_mon_rings(struct dp_soc *soc, uint32_t quota)
{
int ring = 0, work_done, mac_id;
struct dp_pdev *pdev = NULL;
for (ring = 0 ; ring < MAX_PDEV_CNT; ring++) {
pdev = soc->pdev_list[ring];
if (!pdev)
continue;
for (mac_id = 0; mac_id < NUM_RXDMA_RINGS_PER_PDEV; mac_id++) {
int mac_for_pdev = dp_get_mac_id_for_pdev(mac_id,
pdev->pdev_id);
work_done = dp_mon_process(soc, mac_for_pdev,
QCA_NAPI_BUDGET);
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
FL("Reaped %d descs from Monitor rings"),
work_done);
}
}
}
/*
* dp_mon_reap_timer_handler()- timer to reap monitor rings
* reqd as we are not getting ppdu end interrupts
* @arg: SoC Handle
*
* Return:
*
*/
static void dp_mon_reap_timer_handler(void *arg)
{
struct dp_soc *soc = (struct dp_soc *)arg;
dp_service_mon_rings(soc, QCA_NAPI_BUDGET);
qdf_timer_mod(&soc->mon_reap_timer, DP_INTR_POLL_TIMER_MS);
}
#ifndef REMOVE_PKT_LOG
/**
* dp_pkt_log_init() - API to initialize packet log
* @ppdev: physical device handle
* @scn: HIF context
*
* Return: none
*/
void dp_pkt_log_init(struct cdp_pdev *ppdev, void *scn)
{
struct dp_pdev *handle = (struct dp_pdev *)ppdev;
if (handle->pkt_log_init) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
"%s: Packet log not initialized", __func__);
return;
}
pktlog_sethandle(&handle->pl_dev, scn);
pktlog_set_callback_regtype(PKTLOG_DEFAULT_CALLBACK_REGISTRATION);
if (pktlogmod_init(scn)) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
"%s: pktlogmod_init failed", __func__);
handle->pkt_log_init = false;
} else {
handle->pkt_log_init = true;
}
}
/**
* dp_pkt_log_con_service() - connect packet log service
* @ppdev: physical device handle
* @scn: device context
*
* Return: none
*/
static void dp_pkt_log_con_service(struct cdp_pdev *ppdev, void *scn)
{
struct dp_pdev *pdev = (struct dp_pdev *)ppdev;
dp_pkt_log_init((struct cdp_pdev *)pdev, scn);
pktlog_htc_attach();
}
/**
* dp_get_num_rx_contexts() - get number of RX contexts
* @soc_hdl: cdp opaque soc handle
*
* Return: number of RX contexts
*/
static int dp_get_num_rx_contexts(struct cdp_soc_t *soc_hdl)
{
int i;
int num_rx_contexts = 0;
struct dp_soc *soc = (struct dp_soc *)soc_hdl;
for (i = 0; i < wlan_cfg_get_num_contexts(soc->wlan_cfg_ctx); i++)
if (wlan_cfg_get_rx_ring_mask(soc->wlan_cfg_ctx, i))
num_rx_contexts++;
return num_rx_contexts;
}
/**
* dp_pktlogmod_exit() - API to cleanup pktlog info
* @pdev: Pdev handle
*
* Return: none
*/
static void dp_pktlogmod_exit(struct dp_pdev *pdev)
{
struct dp_soc *soc = pdev->soc;
struct hif_opaque_softc *scn = soc->hif_handle;
if (!scn) {
dp_err("Invalid hif(scn) handle");
return;
}
/* stop mon_reap_timer if it has been started */
if (pdev->rx_pktlog_mode != DP_RX_PKTLOG_DISABLED &&
soc->reap_timer_init)
qdf_timer_sync_cancel(&soc->mon_reap_timer);
pktlogmod_exit(scn);
pdev->pkt_log_init = false;
}
#endif
#else
static void dp_pktlogmod_exit(struct dp_pdev *handle) { }
/**
* dp_soc_get_mon_mask_for_interrupt_mode() - get mon mode mask for intr mode
* @soc: pointer to dp_soc handle
* @intr_ctx_num: interrupt context number for which mon mask is needed
*
* Return: mon mask value
*/
static inline
uint32_t dp_soc_get_mon_mask_for_interrupt_mode(struct dp_soc *soc, int intr_ctx_num)
{
return wlan_cfg_get_rx_mon_ring_mask(soc->wlan_cfg_ctx, intr_ctx_num);
}
#endif
/**
* dp_get_dp_vdev_from_cdp_vdev() - get dp_vdev from cdp_vdev by type-casting
* @cdp_opaque_vdev: pointer to cdp_vdev
*
* Return: pointer to dp_vdev
*/
static
struct dp_vdev *dp_get_dp_vdev_from_cdp_vdev(struct cdp_vdev *cdp_opaque_vdev)
{
return (struct dp_vdev *)cdp_opaque_vdev;
}
static int dp_peer_add_ast_wifi3(struct cdp_soc_t *soc_hdl,
struct cdp_peer *peer_hdl,
uint8_t *mac_addr,
enum cdp_txrx_ast_entry_type type,
uint32_t flags)
{
return dp_peer_add_ast((struct dp_soc *)soc_hdl,
(struct dp_peer *)peer_hdl,
mac_addr,
type,
flags);
}
static int dp_peer_update_ast_wifi3(struct cdp_soc_t *soc_hdl,
struct cdp_peer *peer_hdl,
uint8_t *wds_macaddr,
uint32_t flags)
{
int status = -1;
struct dp_soc *soc = (struct dp_soc *)soc_hdl;
struct dp_ast_entry *ast_entry = NULL;
struct dp_peer *peer = (struct dp_peer *)peer_hdl;
qdf_spin_lock_bh(&soc->ast_lock);
ast_entry = dp_peer_ast_hash_find_by_pdevid(soc, wds_macaddr,
peer->vdev->pdev->pdev_id);
if (ast_entry) {
status = dp_peer_update_ast(soc,
peer,
ast_entry, flags);
}
qdf_spin_unlock_bh(&soc->ast_lock);
return status;
}
/*
* dp_wds_reset_ast_wifi3() - Reset the is_active param for ast entry
* @soc_handle: Datapath SOC handle
* @wds_macaddr: WDS entry MAC Address
* Return: None
*/
static void dp_wds_reset_ast_wifi3(struct cdp_soc_t *soc_hdl,
uint8_t *wds_macaddr,
uint8_t *peer_mac_addr,
void *vdev_handle)
{
struct dp_soc *soc = (struct dp_soc *)soc_hdl;
struct dp_ast_entry *ast_entry = NULL;
struct dp_ast_entry *tmp_ast_entry;
struct dp_peer *peer;
struct dp_vdev *vdev = (struct dp_vdev *)vdev_handle;
struct dp_pdev *pdev;
if (!vdev)
return;
pdev = vdev->pdev;
if (peer_mac_addr) {
peer = dp_peer_find_hash_find(soc, peer_mac_addr,
0, vdev->vdev_id);
if (!peer)
return;
qdf_spin_lock_bh(&soc->ast_lock);
DP_PEER_ITERATE_ASE_LIST(peer, ast_entry, tmp_ast_entry) {
if ((ast_entry->type == CDP_TXRX_AST_TYPE_WDS_HM) ||
(ast_entry->type == CDP_TXRX_AST_TYPE_WDS_HM_SEC))
dp_peer_del_ast(soc, ast_entry);
}
qdf_spin_unlock_bh(&soc->ast_lock);
dp_peer_unref_delete(peer);
} else if (wds_macaddr) {
qdf_spin_lock_bh(&soc->ast_lock);
ast_entry = dp_peer_ast_hash_find_by_pdevid(soc, wds_macaddr,
pdev->pdev_id);
if (ast_entry) {
if ((ast_entry->type == CDP_TXRX_AST_TYPE_WDS_HM) ||
(ast_entry->type == CDP_TXRX_AST_TYPE_WDS_HM_SEC))
dp_peer_del_ast(soc, ast_entry);
}
qdf_spin_unlock_bh(&soc->ast_lock);
}
}
/*
* dp_wds_reset_ast_table_wifi3() - Reset the is_active param for all ast entry
* @soc: Datapath SOC handle
*
* Return: None
*/
static void dp_wds_reset_ast_table_wifi3(struct cdp_soc_t *soc_hdl,
void *vdev_hdl)
{
struct dp_soc *soc = (struct dp_soc *) soc_hdl;
struct dp_pdev *pdev;
struct dp_vdev *vdev;
struct dp_peer *peer;
struct dp_ast_entry *ase, *temp_ase;
int i;
qdf_spin_lock_bh(&soc->ast_lock);
for (i = 0; i < MAX_PDEV_CNT && soc->pdev_list[i]; i++) {
pdev = soc->pdev_list[i];
qdf_spin_lock_bh(&pdev->vdev_list_lock);
DP_PDEV_ITERATE_VDEV_LIST(pdev, vdev) {
DP_VDEV_ITERATE_PEER_LIST(vdev, peer) {
DP_PEER_ITERATE_ASE_LIST(peer, ase, temp_ase) {
if ((ase->type ==
CDP_TXRX_AST_TYPE_WDS_HM) ||
(ase->type ==
CDP_TXRX_AST_TYPE_WDS_HM_SEC))
dp_peer_del_ast(soc, ase);
}
}
}
qdf_spin_unlock_bh(&pdev->vdev_list_lock);
}
qdf_spin_unlock_bh(&soc->ast_lock);
}
/*
* dp_wds_flush_ast_table_wifi3() - Delete all wds and hmwds ast entry
* @soc: Datapath SOC handle
*
* Return: None
*/
static void dp_wds_flush_ast_table_wifi3(struct cdp_soc_t *soc_hdl)
{
struct dp_soc *soc = (struct dp_soc *) soc_hdl;
struct dp_pdev *pdev;
struct dp_vdev *vdev;
struct dp_peer *peer;
struct dp_ast_entry *ase, *temp_ase;
int i;
qdf_spin_lock_bh(&soc->ast_lock);
for (i = 0; i < MAX_PDEV_CNT && soc->pdev_list[i]; i++) {
pdev = soc->pdev_list[i];
qdf_spin_lock_bh(&pdev->vdev_list_lock);
DP_PDEV_ITERATE_VDEV_LIST(pdev, vdev) {
DP_VDEV_ITERATE_PEER_LIST(vdev, peer) {
DP_PEER_ITERATE_ASE_LIST(peer, ase, temp_ase) {
if ((ase->type ==
CDP_TXRX_AST_TYPE_STATIC) ||
(ase->type ==
CDP_TXRX_AST_TYPE_SELF) ||
(ase->type ==
CDP_TXRX_AST_TYPE_STA_BSS))
continue;
dp_peer_del_ast(soc, ase);
}
}
}
qdf_spin_unlock_bh(&pdev->vdev_list_lock);
}
qdf_spin_unlock_bh(&soc->ast_lock);
}
/**
* dp_peer_get_ast_info_by_soc_wifi3() - search the soc AST hash table
* and return ast entry information
* of first ast entry found in the
* table with given mac address
*
* @soc : data path soc handle
* @ast_mac_addr : AST entry mac address
* @ast_entry_info : ast entry information
*
* return : true if ast entry found with ast_mac_addr
* false if ast entry not found
*/
static bool dp_peer_get_ast_info_by_soc_wifi3
(struct cdp_soc_t *soc_hdl,
uint8_t *ast_mac_addr,
struct cdp_ast_entry_info *ast_entry_info)
{
struct dp_ast_entry *ast_entry = NULL;
struct dp_soc *soc = (struct dp_soc *)soc_hdl;
qdf_spin_lock_bh(&soc->ast_lock);
ast_entry = dp_peer_ast_hash_find_soc(soc, ast_mac_addr);
if (!ast_entry || !ast_entry->peer) {
qdf_spin_unlock_bh(&soc->ast_lock);
return false;
}
if (ast_entry->delete_in_progress && !ast_entry->callback) {
qdf_spin_unlock_bh(&soc->ast_lock);
return false;
}
ast_entry_info->type = ast_entry->type;
ast_entry_info->pdev_id = ast_entry->pdev_id;
ast_entry_info->vdev_id = ast_entry->vdev_id;
ast_entry_info->peer_id = ast_entry->peer->peer_ids[0];
qdf_mem_copy(&ast_entry_info->peer_mac_addr[0],
&ast_entry->peer->mac_addr.raw[0],
QDF_MAC_ADDR_SIZE);
qdf_spin_unlock_bh(&soc->ast_lock);
return true;
}
/**
* dp_peer_get_ast_info_by_pdevid_wifi3() - search the soc AST hash table
* and return ast entry information
* if mac address and pdev_id matches
*
* @soc : data path soc handle
* @ast_mac_addr : AST entry mac address
* @pdev_id : pdev_id
* @ast_entry_info : ast entry information
*
* return : true if ast entry found with ast_mac_addr
* false if ast entry not found
*/
static bool dp_peer_get_ast_info_by_pdevid_wifi3
(struct cdp_soc_t *soc_hdl,
uint8_t *ast_mac_addr,
uint8_t pdev_id,
struct cdp_ast_entry_info *ast_entry_info)
{
struct dp_ast_entry *ast_entry;
struct dp_soc *soc = (struct dp_soc *)soc_hdl;
qdf_spin_lock_bh(&soc->ast_lock);
ast_entry = dp_peer_ast_hash_find_by_pdevid(soc, ast_mac_addr, pdev_id);
if (!ast_entry || !ast_entry->peer) {
qdf_spin_unlock_bh(&soc->ast_lock);
return false;
}
if (ast_entry->delete_in_progress && !ast_entry->callback) {
qdf_spin_unlock_bh(&soc->ast_lock);
return false;
}
ast_entry_info->type = ast_entry->type;
ast_entry_info->pdev_id = ast_entry->pdev_id;
ast_entry_info->vdev_id = ast_entry->vdev_id;
ast_entry_info->peer_id = ast_entry->peer->peer_ids[0];
qdf_mem_copy(&ast_entry_info->peer_mac_addr[0],
&ast_entry->peer->mac_addr.raw[0],
QDF_MAC_ADDR_SIZE);
qdf_spin_unlock_bh(&soc->ast_lock);
return true;
}
/**
* dp_peer_ast_entry_del_by_soc() - delete the ast entry from soc AST hash table
* with given mac address
*
* @soc : data path soc handle
* @ast_mac_addr : AST entry mac address
* @callback : callback function to called on ast delete response from FW
* @cookie : argument to be passed to callback
*
* return : QDF_STATUS_SUCCESS if ast entry found with ast_mac_addr and delete
* is sent
* QDF_STATUS_E_INVAL false if ast entry not found
*/
static QDF_STATUS dp_peer_ast_entry_del_by_soc(struct cdp_soc_t *soc_handle,
uint8_t *mac_addr,
txrx_ast_free_cb callback,
void *cookie)
{
struct dp_soc *soc = (struct dp_soc *)soc_handle;
struct dp_ast_entry *ast_entry = NULL;
txrx_ast_free_cb cb = NULL;
void *arg = NULL;
qdf_spin_lock_bh(&soc->ast_lock);
ast_entry = dp_peer_ast_hash_find_soc(soc, mac_addr);
if (!ast_entry) {
qdf_spin_unlock_bh(&soc->ast_lock);
return -QDF_STATUS_E_INVAL;
}
if (ast_entry->callback) {
cb = ast_entry->callback;
arg = ast_entry->cookie;
}
ast_entry->callback = callback;
ast_entry->cookie = cookie;
/*
* if delete_in_progress is set AST delete is sent to target
* and host is waiting for response should not send delete
* again
*/
if (!ast_entry->delete_in_progress)
dp_peer_del_ast(soc, ast_entry);
qdf_spin_unlock_bh(&soc->ast_lock);
if (cb) {
cb(soc->ctrl_psoc,
soc,
arg,
CDP_TXRX_AST_DELETE_IN_PROGRESS);
}
return QDF_STATUS_SUCCESS;
}
/**
* dp_peer_ast_entry_del_by_pdev() - delete the ast entry from soc AST hash
* table if mac address and pdev_id matches
*
* @soc : data path soc handle
* @ast_mac_addr : AST entry mac address
* @pdev_id : pdev id
* @callback : callback function to called on ast delete response from FW
* @cookie : argument to be passed to callback
*
* return : QDF_STATUS_SUCCESS if ast entry found with ast_mac_addr and delete
* is sent
* QDF_STATUS_E_INVAL false if ast entry not found
*/
static QDF_STATUS dp_peer_ast_entry_del_by_pdev(struct cdp_soc_t *soc_handle,
uint8_t *mac_addr,
uint8_t pdev_id,
txrx_ast_free_cb callback,
void *cookie)
{
struct dp_soc *soc = (struct dp_soc *)soc_handle;
struct dp_ast_entry *ast_entry;
txrx_ast_free_cb cb = NULL;
void *arg = NULL;
qdf_spin_lock_bh(&soc->ast_lock);
ast_entry = dp_peer_ast_hash_find_by_pdevid(soc, mac_addr, pdev_id);
if (!ast_entry) {
qdf_spin_unlock_bh(&soc->ast_lock);
return -QDF_STATUS_E_INVAL;
}
if (ast_entry->callback) {
cb = ast_entry->callback;
arg = ast_entry->cookie;
}
ast_entry->callback = callback;
ast_entry->cookie = cookie;
/*
* if delete_in_progress is set AST delete is sent to target
* and host is waiting for response should not sent delete
* again
*/
if (!ast_entry->delete_in_progress)
dp_peer_del_ast(soc, ast_entry);
qdf_spin_unlock_bh(&soc->ast_lock);
if (cb) {
cb(soc->ctrl_psoc,
soc,
arg,
CDP_TXRX_AST_DELETE_IN_PROGRESS);
}
return QDF_STATUS_SUCCESS;
}
/**
* dp_srng_find_ring_in_mask() - find which ext_group a ring belongs
* @ring_num: ring num of the ring being queried
* @grp_mask: the grp_mask array for the ring type in question.
*
* The grp_mask array is indexed by group number and the bit fields correspond
* to ring numbers. We are finding which interrupt group a ring belongs to.
*
* Return: the index in the grp_mask array with the ring number.
* -QDF_STATUS_E_NOENT if no entry is found
*/
static int dp_srng_find_ring_in_mask(int ring_num, int *grp_mask)
{
int ext_group_num;
int mask = 1 << ring_num;
for (ext_group_num = 0; ext_group_num < WLAN_CFG_INT_NUM_CONTEXTS;
ext_group_num++) {
if (mask & grp_mask[ext_group_num])
return ext_group_num;
}
return -QDF_STATUS_E_NOENT;
}
static int dp_srng_calculate_msi_group(struct dp_soc *soc,
enum hal_ring_type ring_type,
int ring_num)
{
int *grp_mask;
switch (ring_type) {
case WBM2SW_RELEASE:
/* dp_tx_comp_handler - soc->tx_comp_ring */
if (ring_num < 3)
grp_mask = &soc->wlan_cfg_ctx->int_tx_ring_mask[0];
/* dp_rx_wbm_err_process - soc->rx_rel_ring */
else if (ring_num == 3) {
/* sw treats this as a separate ring type */
grp_mask = &soc->wlan_cfg_ctx->
int_rx_wbm_rel_ring_mask[0];
ring_num = 0;
} else {
qdf_assert(0);
return -QDF_STATUS_E_NOENT;
}
break;
case REO_EXCEPTION:
/* dp_rx_err_process - &soc->reo_exception_ring */
grp_mask = &soc->wlan_cfg_ctx->int_rx_err_ring_mask[0];
break;
case REO_DST:
/* dp_rx_process - soc->reo_dest_ring */
grp_mask = &soc->wlan_cfg_ctx->int_rx_ring_mask[0];
break;
case REO_STATUS:
/* dp_reo_status_ring_handler - soc->reo_status_ring */
grp_mask = &soc->wlan_cfg_ctx->int_reo_status_ring_mask[0];
break;
/* dp_rx_mon_status_srng_process - pdev->rxdma_mon_status_ring*/
case RXDMA_MONITOR_STATUS:
/* dp_rx_mon_dest_process - pdev->rxdma_mon_dst_ring */
case RXDMA_MONITOR_DST:
/* dp_mon_process */
grp_mask = &soc->wlan_cfg_ctx->int_rx_mon_ring_mask[0];
break;
case RXDMA_DST:
/* dp_rxdma_err_process */
grp_mask = &soc->wlan_cfg_ctx->int_rxdma2host_ring_mask[0];
break;
case RXDMA_BUF:
grp_mask = &soc->wlan_cfg_ctx->int_host2rxdma_ring_mask[0];
break;
case RXDMA_MONITOR_BUF:
/* TODO: support low_thresh interrupt */
return -QDF_STATUS_E_NOENT;
break;
case TCL_DATA:
case TCL_CMD:
case REO_CMD:
case SW2WBM_RELEASE:
case WBM_IDLE_LINK:
/* normally empty SW_TO_HW rings */
return -QDF_STATUS_E_NOENT;
break;
case TCL_STATUS:
case REO_REINJECT:
/* misc unused rings */
return -QDF_STATUS_E_NOENT;
break;
case CE_SRC:
case CE_DST:
case CE_DST_STATUS:
/* CE_rings - currently handled by hif */
default:
return -QDF_STATUS_E_NOENT;
break;
}
return dp_srng_find_ring_in_mask(ring_num, grp_mask);
}
static void dp_srng_msi_setup(struct dp_soc *soc, struct hal_srng_params
*ring_params, int ring_type, int ring_num)
{
int msi_group_number;
int msi_data_count;
int ret;
uint32_t msi_data_start, msi_irq_start, addr_low, addr_high;
ret = pld_get_user_msi_assignment(soc->osdev->dev, "DP",
&msi_data_count, &msi_data_start,
&msi_irq_start);
if (ret)
return;
msi_group_number = dp_srng_calculate_msi_group(soc, ring_type,
ring_num);
if (msi_group_number < 0) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_LOW,
FL("ring not part of an ext_group; ring_type: %d,ring_num %d"),
ring_type, ring_num);
ring_params->msi_addr = 0;
ring_params->msi_data = 0;
return;
}
if (msi_group_number > msi_data_count) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_WARN,
FL("2 msi_groups will share an msi; msi_group_num %d"),
msi_group_number);
QDF_ASSERT(0);
}
pld_get_msi_address(soc->osdev->dev, &addr_low, &addr_high);
ring_params->msi_addr = addr_low;
ring_params->msi_addr |= (qdf_dma_addr_t)(((uint64_t)addr_high) << 32);
ring_params->msi_data = (msi_group_number % msi_data_count)
+ msi_data_start;
ring_params->flags |= HAL_SRNG_MSI_INTR;
}
/**
* dp_print_ast_stats() - Dump AST table contents
* @soc: Datapath soc handle
*
* return void
*/
#ifdef FEATURE_AST
void dp_print_ast_stats(struct dp_soc *soc)
{
uint8_t i;
uint8_t num_entries = 0;
struct dp_vdev *vdev;
struct dp_pdev *pdev;
struct dp_peer *peer;
struct dp_ast_entry *ase, *tmp_ase;
char type[CDP_TXRX_AST_TYPE_MAX][10] = {
"NONE", "STATIC", "SELF", "WDS", "MEC", "HMWDS", "BSS",
"DA", "HMWDS_SEC"};
DP_PRINT_STATS("AST Stats:");
DP_PRINT_STATS(" Entries Added = %d", soc->stats.ast.added);
DP_PRINT_STATS(" Entries Deleted = %d", soc->stats.ast.deleted);
DP_PRINT_STATS(" Entries Agedout = %d", soc->stats.ast.aged_out);
DP_PRINT_STATS("AST Table:");
qdf_spin_lock_bh(&soc->ast_lock);
for (i = 0; i < MAX_PDEV_CNT && soc->pdev_list[i]; i++) {
pdev = soc->pdev_list[i];
qdf_spin_lock_bh(&pdev->vdev_list_lock);
DP_PDEV_ITERATE_VDEV_LIST(pdev, vdev) {
DP_VDEV_ITERATE_PEER_LIST(vdev, peer) {
DP_PEER_ITERATE_ASE_LIST(peer, ase, tmp_ase) {
DP_PRINT_STATS("%6d mac_addr = %pM"
" peer_mac_addr = %pM"
" peer_id = %u"
" type = %s"
" next_hop = %d"
" is_active = %d"
" ast_idx = %d"
" ast_hash = %d"
" delete_in_progress = %d"
" pdev_id = %d"
" vdev_id = %d",
++num_entries,
ase->mac_addr.raw,
ase->peer->mac_addr.raw,
ase->peer->peer_ids[0],
type[ase->type],
ase->next_hop,
ase->is_active,
ase->ast_idx,
ase->ast_hash_value,
ase->delete_in_progress,
ase->pdev_id,
vdev->vdev_id);
}
}
}
qdf_spin_unlock_bh(&pdev->vdev_list_lock);
}
qdf_spin_unlock_bh(&soc->ast_lock);
}
#else
void dp_print_ast_stats(struct dp_soc *soc)
{
DP_PRINT_STATS("AST Stats not available.Enable FEATURE_AST");
return;
}
#endif
/**
* dp_print_peer_table() - Dump all Peer stats
* @vdev: Datapath Vdev handle
*
* return void
*/
static void dp_print_peer_table(struct dp_vdev *vdev)
{
struct dp_peer *peer = NULL;
DP_PRINT_STATS("Dumping Peer Table Stats:");
TAILQ_FOREACH(peer, &vdev->peer_list, peer_list_elem) {
if (!peer) {
DP_PRINT_STATS("Invalid Peer");
return;
}
DP_PRINT_STATS(" peer_mac_addr = %pM"
" nawds_enabled = %d"
" bss_peer = %d"
" wapi = %d"
" wds_enabled = %d"
" delete in progress = %d"
" peer id = %d",
peer->mac_addr.raw,
peer->nawds_enabled,
peer->bss_peer,
peer->wapi,
peer->wds_enabled,
peer->delete_in_progress,
peer->peer_ids[0]);
}
}
/*
* dp_srng_mem_alloc() - Allocate memory for SRNG
* @soc : Data path soc handle
* @srng : SRNG pointer
* @align : Align size
*
* return: QDF_STATUS_SUCCESS on successful allocation
* QDF_STATUS_E_NOMEM on failure
*/
static QDF_STATUS
dp_srng_mem_alloc(struct dp_soc *soc, struct dp_srng *srng, uint32_t align,
bool cached)
{
uint32_t align_alloc_size;
if (!cached) {
srng->base_vaddr_unaligned =
qdf_mem_alloc_consistent(soc->osdev,
soc->osdev->dev,
srng->alloc_size,
&srng->base_paddr_unaligned);
} else {
srng->base_vaddr_unaligned = qdf_mem_malloc(srng->alloc_size);
srng->base_paddr_unaligned =
qdf_mem_virt_to_phys(srng->base_vaddr_unaligned);
}
if (!srng->base_vaddr_unaligned) {
return QDF_STATUS_E_NOMEM;
}
/* Re-allocate additional bytes to align base address only if
* above allocation returns unaligned address. Reason for
* trying exact size allocation above is, OS tries to allocate
* blocks of size power-of-2 pages and then free extra pages.
* e.g., of a ring size of 1MB, the allocation below will
* request 1MB plus 7 bytes for alignment, which will cause a
* 2MB block allocation,and that is failing sometimes due to
* memory fragmentation.
* dp_srng_mem_alloc should be replaced with
* qdf_aligned_mem_alloc_consistent after fixing some known
* shortcomings with this QDF function
*/
if ((unsigned long)(srng->base_paddr_unaligned) &
(align - 1)) {
align_alloc_size = srng->alloc_size + align - 1;
if (!cached) {
qdf_mem_free_consistent(soc->osdev, soc->osdev->dev,
srng->alloc_size,
srng->base_vaddr_unaligned,
srng->base_paddr_unaligned, 0);
srng->base_vaddr_unaligned =
qdf_mem_alloc_consistent(soc->osdev,
soc->osdev->dev,
align_alloc_size,
&srng->base_paddr_unaligned);
} else {
qdf_mem_free(srng->base_vaddr_unaligned);
srng->base_vaddr_unaligned =
qdf_mem_malloc(align_alloc_size);
srng->base_paddr_unaligned =
qdf_mem_virt_to_phys(srng->base_vaddr_unaligned);
}
srng->alloc_size = align_alloc_size;
if (!srng->base_vaddr_unaligned) {
return QDF_STATUS_E_NOMEM;
}
}
return QDF_STATUS_SUCCESS;
}
#ifdef WLAN_DP_PER_RING_TYPE_CONFIG
/**
* dp_srng_configure_interrupt_thresholds() - Retrieve interrupt
* threshold values from the wlan_srng_cfg table for each ring type
* @soc: device handle
* @ring_params: per ring specific parameters
* @ring_type: Ring type
* @ring_num: Ring number for a given ring type
*
* Fill the ring params with the interrupt threshold
* configuration parameters available in the per ring type wlan_srng_cfg
* table.
*
* Return: None
*/
static void
dp_srng_configure_interrupt_thresholds(struct dp_soc *soc,
struct hal_srng_params *ring_params,
int ring_type, int ring_num,
int num_entries)
{
if (ring_type == WBM2SW_RELEASE && (ring_num == 3)) {
ring_params->intr_timer_thres_us =
wlan_cfg_get_int_timer_threshold_other(soc->wlan_cfg_ctx);
ring_params->intr_batch_cntr_thres_entries =
wlan_cfg_get_int_batch_threshold_other(soc->wlan_cfg_ctx);
} else {
ring_params->intr_timer_thres_us =
soc->wlan_srng_cfg[ring_type].timer_threshold;
ring_params->intr_batch_cntr_thres_entries =
soc->wlan_srng_cfg[ring_type].batch_count_threshold;
}
ring_params->low_threshold =
soc->wlan_srng_cfg[ring_type].low_threshold;
if (ring_params->low_threshold)
ring_params->flags |= HAL_SRNG_LOW_THRES_INTR_ENABLE;
}
#else
static void
dp_srng_configure_interrupt_thresholds(struct dp_soc *soc,
struct hal_srng_params *ring_params,
int ring_type, int ring_num,
int num_entries)
{
if (ring_type == REO_DST) {
ring_params->intr_timer_thres_us =
wlan_cfg_get_int_timer_threshold_rx(soc->wlan_cfg_ctx);
ring_params->intr_batch_cntr_thres_entries =
wlan_cfg_get_int_batch_threshold_rx(soc->wlan_cfg_ctx);
} else if (ring_type == WBM2SW_RELEASE && (ring_num < 3)) {
ring_params->intr_timer_thres_us =
wlan_cfg_get_int_timer_threshold_tx(soc->wlan_cfg_ctx);
ring_params->intr_batch_cntr_thres_entries =
wlan_cfg_get_int_batch_threshold_tx(soc->wlan_cfg_ctx);
} else {
ring_params->intr_timer_thres_us =
wlan_cfg_get_int_timer_threshold_other(soc->wlan_cfg_ctx);
ring_params->intr_batch_cntr_thres_entries =
wlan_cfg_get_int_batch_threshold_other(soc->wlan_cfg_ctx);
}
/* Enable low threshold interrupts for rx buffer rings (regular and
* monitor buffer rings.
* TODO: See if this is required for any other ring
*/
if ((ring_type == RXDMA_BUF) || (ring_type == RXDMA_MONITOR_BUF) ||
(ring_type == RXDMA_MONITOR_STATUS)) {
/* TODO: Setting low threshold to 1/8th of ring size
* see if this needs to be configurable
*/
ring_params->low_threshold = num_entries >> 3;
ring_params->intr_timer_thres_us =
wlan_cfg_get_int_timer_threshold_rx(soc->wlan_cfg_ctx);
ring_params->flags |= HAL_SRNG_LOW_THRES_INTR_ENABLE;
ring_params->intr_batch_cntr_thres_entries = 0;
}
}
#endif
/**
* dp_srng_setup() - Internal function to setup SRNG rings used by data path
* @soc: datapath soc handle
* @srng: srng handle
* @ring_type: ring that needs to be configured
* @mac_id: mac number
* @num_entries: Total number of entries for a given ring
*
* Return: non-zero - failure/zero - success
*/
static int dp_srng_setup(struct dp_soc *soc, struct dp_srng *srng,
int ring_type, int ring_num, int mac_id,
uint32_t num_entries, bool cached)
{
void *hal_soc = soc->hal_soc;
uint32_t entry_size = hal_srng_get_entrysize(hal_soc, ring_type);
/* TODO: See if we should get align size from hal */
uint32_t ring_base_align = 8;
struct hal_srng_params ring_params;
uint32_t max_entries = hal_srng_max_entries(hal_soc, ring_type);
/* TODO: Currently hal layer takes care of endianness related settings.
* See if these settings need to passed from DP layer
*/
ring_params.flags = 0;
num_entries = (num_entries > max_entries) ? max_entries : num_entries;
srng->hal_srng = NULL;
srng->alloc_size = num_entries * entry_size;
srng->num_entries = num_entries;
if (!dp_is_soc_reinit(soc)) {
if (dp_srng_mem_alloc(soc, srng, ring_base_align, cached) !=
QDF_STATUS_SUCCESS) {
dp_err("alloc failed - ring_type: %d, ring_num %d",
ring_type, ring_num);
return QDF_STATUS_E_NOMEM;
}
}
ring_params.ring_base_paddr =
(qdf_dma_addr_t)qdf_align(
(unsigned long)(srng->base_paddr_unaligned),
ring_base_align);
ring_params.ring_base_vaddr =
(void *)((unsigned long)(srng->base_vaddr_unaligned) +
((unsigned long)(ring_params.ring_base_paddr) -
(unsigned long)(srng->base_paddr_unaligned)));
ring_params.num_entries = num_entries;
dp_verbose_debug("Ring type: %d, num:%d vaddr %pK paddr %pK entries %u",
ring_type, ring_num,
(void *)ring_params.ring_base_vaddr,
(void *)ring_params.ring_base_paddr,
ring_params.num_entries);
if (soc->intr_mode == DP_INTR_MSI) {
dp_srng_msi_setup(soc, &ring_params, ring_type, ring_num);
dp_verbose_debug("Using MSI for ring_type: %d, ring_num %d",
ring_type, ring_num);
} else {
ring_params.msi_data = 0;
ring_params.msi_addr = 0;
dp_verbose_debug("Skipping MSI for ring_type: %d, ring_num %d",
ring_type, ring_num);
}
dp_srng_configure_interrupt_thresholds(soc, &ring_params,
ring_type, ring_num,
num_entries);
if (cached) {
ring_params.flags |= HAL_SRNG_CACHED_DESC;
srng->cached = 1;
}
srng->hal_srng = hal_srng_setup(hal_soc, ring_type, ring_num,
mac_id, &ring_params);
if (!srng->hal_srng) {
if (cached) {
qdf_mem_free(srng->base_vaddr_unaligned);
} else {
qdf_mem_free_consistent(soc->osdev, soc->osdev->dev,
srng->alloc_size,
srng->base_vaddr_unaligned,
srng->base_paddr_unaligned, 0);
}
}
return 0;
}
/*
* dp_srng_deinit() - Internal function to deinit SRNG rings used by data path
* @soc: DP SOC handle
* @srng: source ring structure
* @ring_type: type of ring
* @ring_num: ring number
*
* Return: None
*/
static void dp_srng_deinit(struct dp_soc *soc, struct dp_srng *srng,
int ring_type, int ring_num)
{
if (!srng->hal_srng) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
FL("Ring type: %d, num:%d not setup"),
ring_type, ring_num);
return;
}
hal_srng_cleanup(soc->hal_soc, srng->hal_srng);
srng->hal_srng = NULL;
}
/**
* dp_srng_cleanup - Internal function to cleanup SRNG rings used by data path
* Any buffers allocated and attached to ring entries are expected to be freed
* before calling this function.
*/
static void dp_srng_cleanup(struct dp_soc *soc, struct dp_srng *srng,
int ring_type, int ring_num)
{
if (!dp_is_soc_reinit(soc)) {
if (!srng->hal_srng && (srng->alloc_size == 0)) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
FL("Ring type: %d, num:%d not setup"),
ring_type, ring_num);
return;
}
if (srng->hal_srng) {
hal_srng_cleanup(soc->hal_soc, srng->hal_srng);
srng->hal_srng = NULL;
}
}
if (srng->alloc_size && srng->base_vaddr_unaligned) {
if (!srng->cached) {
qdf_mem_free_consistent(soc->osdev, soc->osdev->dev,
srng->alloc_size,
srng->base_vaddr_unaligned,
srng->base_paddr_unaligned, 0);
} else {
qdf_mem_free(srng->base_vaddr_unaligned);
}
srng->alloc_size = 0;
srng->base_vaddr_unaligned = NULL;
}
srng->hal_srng = NULL;
}
/* TODO: Need this interface from HIF */
void *hif_get_hal_handle(void *hif_handle);
#ifdef WLAN_FEATURE_DP_EVENT_HISTORY
int dp_srng_access_start(struct dp_intr *int_ctx, struct dp_soc *dp_soc,
void *hal_ring)
{
void *hal_soc = dp_soc->hal_soc;
uint32_t hp, tp;
uint8_t ring_id;
hal_get_sw_hptp(hal_soc, hal_ring, &tp, &hp);
ring_id = hal_srng_ring_id_get(hal_ring);
hif_record_event(dp_soc->hif_handle, int_ctx->dp_intr_id,
ring_id, hp, tp, HIF_EVENT_SRNG_ACCESS_START);
return hal_srng_access_start(hal_soc, hal_ring);
}
void dp_srng_access_end(struct dp_intr *int_ctx, struct dp_soc *dp_soc,
void *hal_ring)
{
void *hal_soc = dp_soc->hal_soc;
uint32_t hp, tp;
uint8_t ring_id;
hal_get_sw_hptp(hal_soc, hal_ring, &tp, &hp);
ring_id = hal_srng_ring_id_get(hal_ring);
hif_record_event(dp_soc->hif_handle, int_ctx->dp_intr_id,
ring_id, hp, tp, HIF_EVENT_SRNG_ACCESS_END);
return hal_srng_access_end(hal_soc, hal_ring);
}
#endif /* WLAN_FEATURE_DP_EVENT_HISTORY */
/*
* dp_should_timer_irq_yield() - Decide if the bottom half should yield
* @soc: DP soc handle
* @work_done: work done in softirq context
* @start_time: start time for the softirq
*
* Return: enum with yield code
*/
static enum timer_yield_status
dp_should_timer_irq_yield(struct dp_soc *soc, uint32_t work_done,
uint64_t start_time)
{
uint64_t cur_time = qdf_get_log_timestamp();
if (!work_done)
return DP_TIMER_WORK_DONE;
if (cur_time - start_time > DP_MAX_TIMER_EXEC_TIME_TICKS)
return DP_TIMER_TIME_EXHAUST;
return DP_TIMER_NO_YIELD;
}
/**
* dp_process_lmac_rings() - Process LMAC rings
* @int_ctx: interrupt context
* @total_budget: budget of work which can be done
*
* Return: work done
*/
static int dp_process_lmac_rings(struct dp_intr *int_ctx, int total_budget)
{
struct dp_intr_stats *intr_stats = &int_ctx->intr_stats;
struct dp_soc *soc = int_ctx->soc;
uint32_t remaining_quota = total_budget;
struct dp_pdev *pdev = NULL;
uint32_t work_done = 0;
int budget = total_budget;
int ring = 0;
int mac_id;
/* Process LMAC interrupts */
for (ring = 0 ; ring < MAX_PDEV_CNT; ring++) {
pdev = soc->pdev_list[ring];
if (!pdev)
continue;
for (mac_id = 0; mac_id < NUM_RXDMA_RINGS_PER_PDEV; mac_id++) {
int mac_for_pdev = dp_get_mac_id_for_pdev(mac_id,
pdev->pdev_id);
if (int_ctx->rx_mon_ring_mask & (1 << mac_for_pdev)) {
work_done = dp_mon_process(soc, mac_for_pdev,
remaining_quota);
if (work_done)
intr_stats->num_rx_mon_ring_masks++;
budget -= work_done;
if (budget <= 0)
goto budget_done;
remaining_quota = budget;
}
if (int_ctx->rxdma2host_ring_mask &
(1 << mac_for_pdev)) {
work_done = dp_rxdma_err_process(int_ctx, soc,
mac_for_pdev,
remaining_quota);
if (work_done)
intr_stats->num_rxdma2host_ring_masks++;
budget -= work_done;
if (budget <= 0)
goto budget_done;
remaining_quota = budget;
}
if (int_ctx->host2rxdma_ring_mask &
(1 << mac_for_pdev)) {
union dp_rx_desc_list_elem_t *desc_list = NULL;
union dp_rx_desc_list_elem_t *tail = NULL;
struct dp_srng *rx_refill_buf_ring =
&pdev->rx_refill_buf_ring;
intr_stats->num_host2rxdma_ring_masks++;
DP_STATS_INC(pdev, replenish.low_thresh_intrs,
1);
dp_rx_buffers_replenish(soc, mac_for_pdev,
rx_refill_buf_ring,
&soc->rx_desc_buf[mac_for_pdev],
0, &desc_list, &tail);
}
}
}
budget_done:
return total_budget - budget;
}
/*
* dp_service_srngs() - Top level interrupt handler for DP Ring interrupts
* @dp_ctx: DP SOC handle
* @budget: Number of frames/descriptors that can be processed in one shot
*
* Return: remaining budget/quota for the soc device
*/
static uint32_t dp_service_srngs(void *dp_ctx, uint32_t dp_budget)
{
struct dp_intr *int_ctx = (struct dp_intr *)dp_ctx;
struct dp_intr_stats *intr_stats = &int_ctx->intr_stats;
struct dp_soc *soc = int_ctx->soc;
int ring = 0;
uint32_t work_done = 0;
int budget = dp_budget;
uint8_t tx_mask = int_ctx->tx_ring_mask;
uint8_t rx_mask = int_ctx->rx_ring_mask;
uint8_t rx_err_mask = int_ctx->rx_err_ring_mask;
uint8_t rx_wbm_rel_mask = int_ctx->rx_wbm_rel_ring_mask;
uint8_t reo_status_mask = int_ctx->reo_status_ring_mask;
uint32_t remaining_quota = dp_budget;
dp_verbose_debug("tx %x rx %x rx_err %x rx_wbm_rel %x reo_status %x rx_mon_ring %x host2rxdma %x rxdma2host %x\n",
tx_mask, rx_mask, rx_err_mask, rx_wbm_rel_mask,
reo_status_mask,
int_ctx->rx_mon_ring_mask,
int_ctx->host2rxdma_ring_mask,
int_ctx->rxdma2host_ring_mask);
/* Process Tx completion interrupts first to return back buffers */
while (tx_mask) {
if (tx_mask & 0x1) {
work_done = dp_tx_comp_handler(int_ctx,
soc,
soc->tx_comp_ring[ring].hal_srng,
ring, remaining_quota);
if (work_done) {
intr_stats->num_tx_ring_masks[ring]++;
dp_verbose_debug("tx mask 0x%x ring %d, budget %d, work_done %d",
tx_mask, ring, budget,
work_done);
}
budget -= work_done;
if (budget <= 0)
goto budget_done;
remaining_quota = budget;
}
tx_mask = tx_mask >> 1;
ring++;
}
/* Process REO Exception ring interrupt */
if (rx_err_mask) {
work_done = dp_rx_err_process(int_ctx, soc,
soc->reo_exception_ring.hal_srng,
remaining_quota);
if (work_done) {
intr_stats->num_rx_err_ring_masks++;
dp_verbose_debug("REO Exception Ring: work_done %d budget %d",
work_done, budget);
}
budget -= work_done;
if (budget <= 0) {
goto budget_done;
}
remaining_quota = budget;
}
/* Process Rx WBM release ring interrupt */
if (rx_wbm_rel_mask) {
work_done = dp_rx_wbm_err_process(int_ctx, soc,
soc->rx_rel_ring.hal_srng,
remaining_quota);
if (work_done) {
intr_stats->num_rx_wbm_rel_ring_masks++;
dp_verbose_debug("WBM Release Ring: work_done %d budget %d",
work_done, budget);
}
budget -= work_done;
if (budget <= 0) {
goto budget_done;
}
remaining_quota = budget;
}
/* Process Rx interrupts */
if (rx_mask) {
for (ring = 0; ring < soc->num_reo_dest_rings; ring++) {
if (!(rx_mask & (1 << ring)))
continue;
work_done = dp_rx_process(int_ctx,
soc->reo_dest_ring[ring].hal_srng,
ring,
remaining_quota);
if (work_done) {
intr_stats->num_rx_ring_masks[ring]++;
dp_verbose_debug("rx mask 0x%x ring %d, work_done %d budget %d",
rx_mask, ring,
work_done, budget);
budget -= work_done;
if (budget <= 0)
goto budget_done;
remaining_quota = budget;
}
}
}
if (reo_status_mask) {
if (dp_reo_status_ring_handler(int_ctx, soc))
int_ctx->intr_stats.num_reo_status_ring_masks++;
}
work_done = dp_process_lmac_rings(int_ctx, remaining_quota);
if (work_done) {
budget -= work_done;
if (budget <= 0)
goto budget_done;
remaining_quota = budget;
}
qdf_lro_flush(int_ctx->lro_ctx);
intr_stats->num_masks++;
budget_done:
return dp_budget - budget;
}
/* dp_interrupt_timer()- timer poll for interrupts
*
* @arg: SoC Handle
*
* Return:
*
*/
static void dp_interrupt_timer(void *arg)
{
struct dp_soc *soc = (struct dp_soc *) arg;
enum timer_yield_status yield = DP_TIMER_NO_YIELD;
uint32_t work_done = 0, total_work_done = 0;
int budget = 0xffff;
uint32_t remaining_quota = budget;
uint64_t start_time;
int i;
if (!qdf_atomic_read(&soc->cmn_init_done))
return;
start_time = qdf_get_log_timestamp();
while (yield == DP_TIMER_NO_YIELD) {
for (i = 0;
i < wlan_cfg_get_num_contexts(soc->wlan_cfg_ctx); i++) {
if (!soc->intr_ctx[i].rx_mon_ring_mask)
continue;
work_done = dp_process_lmac_rings(&soc->intr_ctx[i],
remaining_quota);
if (work_done) {
budget -= work_done;
if (budget <= 0) {
yield = DP_TIMER_WORK_EXHAUST;
goto budget_done;
}
remaining_quota = budget;
total_work_done += work_done;
}
}
yield = dp_should_timer_irq_yield(soc, total_work_done,
start_time);
total_work_done = 0;
}
budget_done:
if (yield == DP_TIMER_WORK_EXHAUST ||
yield == DP_TIMER_TIME_EXHAUST)
qdf_timer_mod(&soc->int_timer, 1);
else
qdf_timer_mod(&soc->int_timer, DP_INTR_POLL_TIMER_MS);
}
/*
* dp_soc_attach_poll() - Register handlers for DP interrupts
* @txrx_soc: DP SOC handle
*
* Host driver will register for “DP_NUM_INTERRUPT_CONTEXTS” number of NAPI
* contexts. Each NAPI context will have a tx_ring_mask , rx_ring_mask ,and
* rx_monitor_ring mask to indicate the rings that are processed by the handler.
*
* Return: 0 for success, nonzero for failure.
*/
static QDF_STATUS dp_soc_attach_poll(void *txrx_soc)
{
struct dp_soc *soc = (struct dp_soc *)txrx_soc;
int i;
soc->intr_mode = DP_INTR_POLL;
for (i = 0; i < wlan_cfg_get_num_contexts(soc->wlan_cfg_ctx); i++) {
soc->intr_ctx[i].dp_intr_id = i;
soc->intr_ctx[i].tx_ring_mask =
wlan_cfg_get_tx_ring_mask(soc->wlan_cfg_ctx, i);
soc->intr_ctx[i].rx_ring_mask =
wlan_cfg_get_rx_ring_mask(soc->wlan_cfg_ctx, i);
soc->intr_ctx[i].rx_mon_ring_mask =
wlan_cfg_get_rx_mon_ring_mask(soc->wlan_cfg_ctx, i);
soc->intr_ctx[i].rx_err_ring_mask =
wlan_cfg_get_rx_err_ring_mask(soc->wlan_cfg_ctx, i);
soc->intr_ctx[i].rx_wbm_rel_ring_mask =
wlan_cfg_get_rx_wbm_rel_ring_mask(soc->wlan_cfg_ctx, i);
soc->intr_ctx[i].reo_status_ring_mask =
wlan_cfg_get_reo_status_ring_mask(soc->wlan_cfg_ctx, i);
soc->intr_ctx[i].rxdma2host_ring_mask =
wlan_cfg_get_rxdma2host_ring_mask(soc->wlan_cfg_ctx, i);
soc->intr_ctx[i].soc = soc;
soc->intr_ctx[i].lro_ctx = qdf_lro_init();
}
qdf_timer_init(soc->osdev, &soc->int_timer,
dp_interrupt_timer, (void *)soc,
QDF_TIMER_TYPE_WAKE_APPS);
return QDF_STATUS_SUCCESS;
}
/**
* dp_soc_set_interrupt_mode() - Set the interrupt mode in soc
* soc: DP soc handle
*
* Set the appropriate interrupt mode flag in the soc
*/
static void dp_soc_set_interrupt_mode(struct dp_soc *soc)
{
uint32_t msi_base_data, msi_vector_start;
int msi_vector_count, ret;
soc->intr_mode = DP_INTR_LEGACY;
if (!(soc->wlan_cfg_ctx->napi_enabled) ||
(soc->cdp_soc.ol_ops->get_con_mode &&
soc->cdp_soc.ol_ops->get_con_mode() == QDF_GLOBAL_MONITOR_MODE)) {
soc->intr_mode = DP_INTR_POLL;
} else {
ret = pld_get_user_msi_assignment(soc->osdev->dev, "DP",
&msi_vector_count,
&msi_base_data,
&msi_vector_start);
if (ret)
return;
soc->intr_mode = DP_INTR_MSI;
}
}
static QDF_STATUS dp_soc_interrupt_attach(void *txrx_soc);
#if defined(CONFIG_MCL)
/*
* dp_soc_interrupt_attach_wrapper() - Register handlers for DP interrupts
* @txrx_soc: DP SOC handle
*
* Call the appropriate attach function based on the mode of operation.
* This is a WAR for enabling monitor mode.
*
* Return: 0 for success. nonzero for failure.
*/
static QDF_STATUS dp_soc_interrupt_attach_wrapper(void *txrx_soc)
{
struct dp_soc *soc = (struct dp_soc *)txrx_soc;
if (!(soc->wlan_cfg_ctx->napi_enabled) ||
(soc->cdp_soc.ol_ops->get_con_mode &&
soc->cdp_soc.ol_ops->get_con_mode() ==
QDF_GLOBAL_MONITOR_MODE)) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
"%s: Poll mode", __func__);
return dp_soc_attach_poll(txrx_soc);
} else {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
"%s: Interrupt mode", __func__);
return dp_soc_interrupt_attach(txrx_soc);
}
}
#else
#if defined(DP_INTR_POLL_BASED) && DP_INTR_POLL_BASED
static QDF_STATUS dp_soc_interrupt_attach_wrapper(void *txrx_soc)
{
return dp_soc_attach_poll(txrx_soc);
}
#else
static QDF_STATUS dp_soc_interrupt_attach_wrapper(void *txrx_soc)
{
struct dp_soc *soc = (struct dp_soc *)txrx_soc;
if (hif_is_polled_mode_enabled(soc->hif_handle))
return dp_soc_attach_poll(txrx_soc);
else
return dp_soc_interrupt_attach(txrx_soc);
}
#endif
#endif
static void dp_soc_interrupt_map_calculate_integrated(struct dp_soc *soc,
int intr_ctx_num, int *irq_id_map, int *num_irq_r)
{
int j;
int num_irq = 0;
int tx_mask =
wlan_cfg_get_tx_ring_mask(soc->wlan_cfg_ctx, intr_ctx_num);
int rx_mask =
wlan_cfg_get_rx_ring_mask(soc->wlan_cfg_ctx, intr_ctx_num);
int rx_mon_mask =
wlan_cfg_get_rx_mon_ring_mask(soc->wlan_cfg_ctx, intr_ctx_num);
int rx_err_ring_mask = wlan_cfg_get_rx_err_ring_mask(
soc->wlan_cfg_ctx, intr_ctx_num);
int rx_wbm_rel_ring_mask = wlan_cfg_get_rx_wbm_rel_ring_mask(
soc->wlan_cfg_ctx, intr_ctx_num);
int reo_status_ring_mask = wlan_cfg_get_reo_status_ring_mask(
soc->wlan_cfg_ctx, intr_ctx_num);
int rxdma2host_ring_mask = wlan_cfg_get_rxdma2host_ring_mask(
soc->wlan_cfg_ctx, intr_ctx_num);
int host2rxdma_ring_mask = wlan_cfg_get_host2rxdma_ring_mask(
soc->wlan_cfg_ctx, intr_ctx_num);
int host2rxdma_mon_ring_mask = wlan_cfg_get_host2rxdma_mon_ring_mask(
soc->wlan_cfg_ctx, intr_ctx_num);
soc->intr_mode = DP_INTR_LEGACY;
for (j = 0; j < HIF_MAX_GRP_IRQ; j++) {
if (tx_mask & (1 << j)) {
irq_id_map[num_irq++] =
(wbm2host_tx_completions_ring1 - j);
}
if (rx_mask & (1 << j)) {
irq_id_map[num_irq++] =
(reo2host_destination_ring1 - j);
}
if (rxdma2host_ring_mask & (1 << j)) {
irq_id_map[num_irq++] =
rxdma2host_destination_ring_mac1 -
wlan_cfg_get_hw_mac_idx(soc->wlan_cfg_ctx, j);
}
if (host2rxdma_ring_mask & (1 << j)) {
irq_id_map[num_irq++] =
host2rxdma_host_buf_ring_mac1 -
wlan_cfg_get_hw_mac_idx(soc->wlan_cfg_ctx, j);
}
if (host2rxdma_mon_ring_mask & (1 << j)) {
irq_id_map[num_irq++] =
host2rxdma_monitor_ring1 -
wlan_cfg_get_hw_mac_idx(soc->wlan_cfg_ctx, j);
}
if (rx_mon_mask & (1 << j)) {
irq_id_map[num_irq++] =
ppdu_end_interrupts_mac1 -
wlan_cfg_get_hw_mac_idx(soc->wlan_cfg_ctx, j);
irq_id_map[num_irq++] =
rxdma2host_monitor_status_ring_mac1 -
wlan_cfg_get_hw_mac_idx(soc->wlan_cfg_ctx, j);
}
if (rx_wbm_rel_ring_mask & (1 << j))
irq_id_map[num_irq++] = wbm2host_rx_release;
if (rx_err_ring_mask & (1 << j))
irq_id_map[num_irq++] = reo2host_exception;
if (reo_status_ring_mask & (1 << j))
irq_id_map[num_irq++] = reo2host_status;
}
*num_irq_r = num_irq;
}
static void dp_soc_interrupt_map_calculate_msi(struct dp_soc *soc,
int intr_ctx_num, int *irq_id_map, int *num_irq_r,
int msi_vector_count, int msi_vector_start)
{
int tx_mask = wlan_cfg_get_tx_ring_mask(
soc->wlan_cfg_ctx, intr_ctx_num);
int rx_mask = wlan_cfg_get_rx_ring_mask(
soc->wlan_cfg_ctx, intr_ctx_num);
int rx_mon_mask = wlan_cfg_get_rx_mon_ring_mask(
soc->wlan_cfg_ctx, intr_ctx_num);
int rx_err_ring_mask = wlan_cfg_get_rx_err_ring_mask(
soc->wlan_cfg_ctx, intr_ctx_num);
int rx_wbm_rel_ring_mask = wlan_cfg_get_rx_wbm_rel_ring_mask(
soc->wlan_cfg_ctx, intr_ctx_num);
int reo_status_ring_mask = wlan_cfg_get_reo_status_ring_mask(
soc->wlan_cfg_ctx, intr_ctx_num);
int rxdma2host_ring_mask = wlan_cfg_get_rxdma2host_ring_mask(
soc->wlan_cfg_ctx, intr_ctx_num);
unsigned int vector =
(intr_ctx_num % msi_vector_count) + msi_vector_start;
int num_irq = 0;
soc->intr_mode = DP_INTR_MSI;
if (tx_mask | rx_mask | rx_mon_mask | rx_err_ring_mask |
rx_wbm_rel_ring_mask | reo_status_ring_mask | rxdma2host_ring_mask)
irq_id_map[num_irq++] =
pld_get_msi_irq(soc->osdev->dev, vector);
*num_irq_r = num_irq;
}
static void dp_soc_interrupt_map_calculate(struct dp_soc *soc, int intr_ctx_num,
int *irq_id_map, int *num_irq)
{
int msi_vector_count, ret;
uint32_t msi_base_data, msi_vector_start;
ret = pld_get_user_msi_assignment(soc->osdev->dev, "DP",
&msi_vector_count,
&msi_base_data,
&msi_vector_start);
if (ret)
return dp_soc_interrupt_map_calculate_integrated(soc,
intr_ctx_num, irq_id_map, num_irq);
else
dp_soc_interrupt_map_calculate_msi(soc,
intr_ctx_num, irq_id_map, num_irq,
msi_vector_count, msi_vector_start);
}
/*
* dp_soc_interrupt_attach() - Register handlers for DP interrupts
* @txrx_soc: DP SOC handle
*
* Host driver will register for “DP_NUM_INTERRUPT_CONTEXTS” number of NAPI
* contexts. Each NAPI context will have a tx_ring_mask , rx_ring_mask ,and
* rx_monitor_ring mask to indicate the rings that are processed by the handler.
*
* Return: 0 for success. nonzero for failure.
*/
static QDF_STATUS dp_soc_interrupt_attach(void *txrx_soc)
{
struct dp_soc *soc = (struct dp_soc *)txrx_soc;
int i = 0;
int num_irq = 0;
for (i = 0; i < wlan_cfg_get_num_contexts(soc->wlan_cfg_ctx); i++) {
int ret = 0;
/* Map of IRQ ids registered with one interrupt context */
int irq_id_map[HIF_MAX_GRP_IRQ];
int tx_mask =
wlan_cfg_get_tx_ring_mask(soc->wlan_cfg_ctx, i);
int rx_mask =
wlan_cfg_get_rx_ring_mask(soc->wlan_cfg_ctx, i);
int rx_mon_mask =
dp_soc_get_mon_mask_for_interrupt_mode(soc, i);
int rx_err_ring_mask =
wlan_cfg_get_rx_err_ring_mask(soc->wlan_cfg_ctx, i);
int rx_wbm_rel_ring_mask =
wlan_cfg_get_rx_wbm_rel_ring_mask(soc->wlan_cfg_ctx, i);
int reo_status_ring_mask =
wlan_cfg_get_reo_status_ring_mask(soc->wlan_cfg_ctx, i);
int rxdma2host_ring_mask =
wlan_cfg_get_rxdma2host_ring_mask(soc->wlan_cfg_ctx, i);
int host2rxdma_ring_mask =
wlan_cfg_get_host2rxdma_ring_mask(soc->wlan_cfg_ctx, i);
int host2rxdma_mon_ring_mask =
wlan_cfg_get_host2rxdma_mon_ring_mask(
soc->wlan_cfg_ctx, i);
soc->intr_ctx[i].dp_intr_id = i;
soc->intr_ctx[i].tx_ring_mask = tx_mask;
soc->intr_ctx[i].rx_ring_mask = rx_mask;
soc->intr_ctx[i].rx_mon_ring_mask = rx_mon_mask;
soc->intr_ctx[i].rx_err_ring_mask = rx_err_ring_mask;
soc->intr_ctx[i].rxdma2host_ring_mask = rxdma2host_ring_mask;
soc->intr_ctx[i].host2rxdma_ring_mask = host2rxdma_ring_mask;
soc->intr_ctx[i].rx_wbm_rel_ring_mask = rx_wbm_rel_ring_mask;
soc->intr_ctx[i].reo_status_ring_mask = reo_status_ring_mask;
soc->intr_ctx[i].host2rxdma_mon_ring_mask =
host2rxdma_mon_ring_mask;
soc->intr_ctx[i].soc = soc;
num_irq = 0;
dp_soc_interrupt_map_calculate(soc, i, &irq_id_map[0],
&num_irq);
ret = hif_register_ext_group(soc->hif_handle,
num_irq, irq_id_map, dp_service_srngs,
&soc->intr_ctx[i], "dp_intr",
HIF_EXEC_NAPI_TYPE, QCA_NAPI_DEF_SCALE_BIN_SHIFT);
if (ret) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
FL("failed, ret = %d"), ret);
return QDF_STATUS_E_FAILURE;
}
soc->intr_ctx[i].lro_ctx = qdf_lro_init();
}
hif_configure_ext_group_interrupts(soc->hif_handle);
hif_config_irq_set_perf_affinity_hint(soc->hif_handle);
return QDF_STATUS_SUCCESS;
}
/*
* dp_soc_interrupt_detach() - Deregister any allocations done for interrupts
* @txrx_soc: DP SOC handle
*
* Return: void
*/
static void dp_soc_interrupt_detach(void *txrx_soc)
{
struct dp_soc *soc = (struct dp_soc *)txrx_soc;
int i;
if (soc->intr_mode == DP_INTR_POLL) {
qdf_timer_free(&soc->int_timer);
} else {
hif_deregister_exec_group(soc->hif_handle, "dp_intr");
}
for (i = 0; i < wlan_cfg_get_num_contexts(soc->wlan_cfg_ctx); i++) {
soc->intr_ctx[i].tx_ring_mask = 0;
soc->intr_ctx[i].rx_ring_mask = 0;
soc->intr_ctx[i].rx_mon_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;
soc->intr_ctx[i].rxdma2host_ring_mask = 0;
soc->intr_ctx[i].host2rxdma_ring_mask = 0;
soc->intr_ctx[i].host2rxdma_mon_ring_mask = 0;
qdf_lro_deinit(soc->intr_ctx[i].lro_ctx);
}
}
#define AVG_MAX_MPDUS_PER_TID 128
#define AVG_TIDS_PER_CLIENT 2
#define AVG_FLOWS_PER_TID 2
#define AVG_MSDUS_PER_FLOW 128
#define AVG_MSDUS_PER_MPDU 4
/*
* Allocate and setup link descriptor pool that will be used by HW for
* various link and queue descriptors and managed by WBM
*/
static int dp_hw_link_desc_pool_setup(struct dp_soc *soc)
{
int link_desc_size = hal_get_link_desc_size(soc->hal_soc);
int link_desc_align = hal_get_link_desc_align(soc->hal_soc);
uint32_t max_clients = wlan_cfg_get_max_clients(soc->wlan_cfg_ctx);
uint32_t num_mpdus_per_link_desc =
hal_num_mpdus_per_link_desc(soc->hal_soc);
uint32_t num_msdus_per_link_desc =
hal_num_msdus_per_link_desc(soc->hal_soc);
uint32_t num_mpdu_links_per_queue_desc =
hal_num_mpdu_links_per_queue_desc(soc->hal_soc);
uint32_t max_alloc_size = wlan_cfg_max_alloc_size(soc->wlan_cfg_ctx);
uint32_t total_link_descs, total_mem_size;
uint32_t num_mpdu_link_descs, num_mpdu_queue_descs;
uint32_t num_tx_msdu_link_descs, num_rx_msdu_link_descs;
uint32_t num_link_desc_banks;
uint32_t last_bank_size = 0;
uint32_t entry_size, num_entries;
int i;
uint32_t desc_id = 0;
qdf_dma_addr_t *baseaddr = NULL;
/* Only Tx queue descriptors are allocated from common link descriptor
* pool Rx queue descriptors are not included in this because (REO queue
* extension descriptors) they are expected to be allocated contiguously
* with REO queue descriptors
*/
num_mpdu_link_descs = (max_clients * AVG_TIDS_PER_CLIENT *
AVG_MAX_MPDUS_PER_TID) / num_mpdus_per_link_desc;
num_mpdu_queue_descs = num_mpdu_link_descs /
num_mpdu_links_per_queue_desc;
num_tx_msdu_link_descs = (max_clients * AVG_TIDS_PER_CLIENT *
AVG_FLOWS_PER_TID * AVG_MSDUS_PER_FLOW) /
num_msdus_per_link_desc;
num_rx_msdu_link_descs = (max_clients * AVG_TIDS_PER_CLIENT *
AVG_MAX_MPDUS_PER_TID * AVG_MSDUS_PER_MPDU) / 6;
num_entries = num_mpdu_link_descs + num_mpdu_queue_descs +
num_tx_msdu_link_descs + num_rx_msdu_link_descs;
/* Round up to power of 2 */
total_link_descs = 1;
while (total_link_descs < num_entries)
total_link_descs <<= 1;
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_HIGH,
FL("total_link_descs: %u, link_desc_size: %d"),
total_link_descs, link_desc_size);
total_mem_size = total_link_descs * link_desc_size;
total_mem_size += link_desc_align;
if (total_mem_size <= max_alloc_size) {
num_link_desc_banks = 0;
last_bank_size = total_mem_size;
} else {
num_link_desc_banks = (total_mem_size) /
(max_alloc_size - link_desc_align);
last_bank_size = total_mem_size %
(max_alloc_size - link_desc_align);
}
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_HIGH,
FL("total_mem_size: %d, num_link_desc_banks: %u"),
total_mem_size, num_link_desc_banks);
for (i = 0; i < num_link_desc_banks; i++) {
if (!dp_is_soc_reinit(soc)) {
baseaddr = &soc->link_desc_banks[i].
base_paddr_unaligned;
soc->link_desc_banks[i].base_vaddr_unaligned =
qdf_mem_alloc_consistent(soc->osdev,
soc->osdev->dev,
max_alloc_size,
baseaddr);
}
soc->link_desc_banks[i].size = max_alloc_size;
soc->link_desc_banks[i].base_vaddr = (void *)((unsigned long)(
soc->link_desc_banks[i].base_vaddr_unaligned) +
((unsigned long)(
soc->link_desc_banks[i].base_vaddr_unaligned) %
link_desc_align));
soc->link_desc_banks[i].base_paddr = (unsigned long)(
soc->link_desc_banks[i].base_paddr_unaligned) +
((unsigned long)(soc->link_desc_banks[i].base_vaddr) -
(unsigned long)(
soc->link_desc_banks[i].base_vaddr_unaligned));
if (!soc->link_desc_banks[i].base_vaddr_unaligned) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
FL("Link descriptor memory alloc failed"));
goto fail;
}
qdf_minidump_log((void *)(soc->link_desc_banks[i].base_vaddr),
soc->link_desc_banks[i].size, "link_desc_bank");
}
if (last_bank_size) {
/* Allocate last bank in case total memory required is not exact
* multiple of max_alloc_size
*/
if (!dp_is_soc_reinit(soc)) {
baseaddr = &soc->link_desc_banks[i].
base_paddr_unaligned;
soc->link_desc_banks[i].base_vaddr_unaligned =
qdf_mem_alloc_consistent(soc->osdev,
soc->osdev->dev,
last_bank_size,
baseaddr);
}
soc->link_desc_banks[i].size = last_bank_size;
soc->link_desc_banks[i].base_vaddr = (void *)((unsigned long)
(soc->link_desc_banks[i].base_vaddr_unaligned) +
((unsigned long)(
soc->link_desc_banks[i].base_vaddr_unaligned) %
link_desc_align));
soc->link_desc_banks[i].base_paddr =
(unsigned long)(
soc->link_desc_banks[i].base_paddr_unaligned) +
((unsigned long)(soc->link_desc_banks[i].base_vaddr) -
(unsigned long)(
soc->link_desc_banks[i].base_vaddr_unaligned));
qdf_minidump_log((void *)(soc->link_desc_banks[i].base_vaddr),
soc->link_desc_banks[i].size, "link_desc_bank");
}
/* Allocate and setup link descriptor idle list for HW internal use */
entry_size = hal_srng_get_entrysize(soc->hal_soc, WBM_IDLE_LINK);
total_mem_size = entry_size * total_link_descs;
if (total_mem_size <= max_alloc_size) {
void *desc;
if (dp_srng_setup(soc, &soc->wbm_idle_link_ring,
WBM_IDLE_LINK, 0, 0, total_link_descs, 0)) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
FL("Link desc idle ring setup failed"));
goto fail;
}
qdf_minidump_log(
(void *)(soc->wbm_idle_link_ring.base_vaddr_unaligned),
soc->wbm_idle_link_ring.alloc_size,
"wbm_idle_link_ring");
hal_srng_access_start_unlocked(soc->hal_soc,
soc->wbm_idle_link_ring.hal_srng);
for (i = 0; i < MAX_LINK_DESC_BANKS &&
soc->link_desc_banks[i].base_paddr; i++) {
uint32_t num_entries = (soc->link_desc_banks[i].size -
((unsigned long)(
soc->link_desc_banks[i].base_vaddr) -
(unsigned long)(
soc->link_desc_banks[i].base_vaddr_unaligned)))
/ link_desc_size;
unsigned long paddr = (unsigned long)(
soc->link_desc_banks[i].base_paddr);
while (num_entries && (desc = hal_srng_src_get_next(
soc->hal_soc,
soc->wbm_idle_link_ring.hal_srng))) {
hal_set_link_desc_addr(desc,
LINK_DESC_COOKIE(desc_id, i), paddr);
num_entries--;
desc_id++;
paddr += link_desc_size;
}
}
hal_srng_access_end_unlocked(soc->hal_soc,
soc->wbm_idle_link_ring.hal_srng);
} else {
uint32_t num_scatter_bufs;
uint32_t num_entries_per_buf;
uint32_t rem_entries;
uint8_t *scatter_buf_ptr;
uint16_t scatter_buf_num;
uint32_t buf_size = 0;
soc->wbm_idle_scatter_buf_size =
hal_idle_list_scatter_buf_size(soc->hal_soc);
num_entries_per_buf = hal_idle_scatter_buf_num_entries(
soc->hal_soc, soc->wbm_idle_scatter_buf_size);
num_scatter_bufs = hal_idle_list_num_scatter_bufs(
soc->hal_soc, total_mem_size,
soc->wbm_idle_scatter_buf_size);
if (num_scatter_bufs > MAX_IDLE_SCATTER_BUFS) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
FL("scatter bufs size out of bounds"));
goto fail;
}
for (i = 0; i < num_scatter_bufs; i++) {
baseaddr = &soc->wbm_idle_scatter_buf_base_paddr[i];
if (!dp_is_soc_reinit(soc)) {
buf_size = soc->wbm_idle_scatter_buf_size;
soc->wbm_idle_scatter_buf_base_vaddr[i] =
qdf_mem_alloc_consistent(soc->osdev,
soc->osdev->
dev,
buf_size,
baseaddr);
}
if (!soc->wbm_idle_scatter_buf_base_vaddr[i]) {
QDF_TRACE(QDF_MODULE_ID_DP,
QDF_TRACE_LEVEL_ERROR,
FL("Scatter lst memory alloc fail"));
goto fail;
}
}
/* Populate idle list scatter buffers with link descriptor
* pointers
*/
scatter_buf_num = 0;
scatter_buf_ptr = (uint8_t *)(
soc->wbm_idle_scatter_buf_base_vaddr[scatter_buf_num]);
rem_entries = num_entries_per_buf;
for (i = 0; i < MAX_LINK_DESC_BANKS &&
soc->link_desc_banks[i].base_paddr; i++) {
uint32_t num_link_descs =
(soc->link_desc_banks[i].size -
((unsigned long)(
soc->link_desc_banks[i].base_vaddr) -
(unsigned long)(
soc->link_desc_banks[i].base_vaddr_unaligned)))
/ link_desc_size;
unsigned long paddr = (unsigned long)(
soc->link_desc_banks[i].base_paddr);
while (num_link_descs) {
hal_set_link_desc_addr((void *)scatter_buf_ptr,
LINK_DESC_COOKIE(desc_id, i), paddr);
num_link_descs--;
desc_id++;
paddr += link_desc_size;
rem_entries--;
if (rem_entries) {
scatter_buf_ptr += entry_size;
} else {
rem_entries = num_entries_per_buf;
scatter_buf_num++;
if (scatter_buf_num >= num_scatter_bufs)
break;
scatter_buf_ptr = (uint8_t *)(
soc->wbm_idle_scatter_buf_base_vaddr[
scatter_buf_num]);
}
}
}
/* Setup link descriptor idle list in HW */
hal_setup_link_idle_list(soc->hal_soc,
soc->wbm_idle_scatter_buf_base_paddr,
soc->wbm_idle_scatter_buf_base_vaddr,
num_scatter_bufs, soc->wbm_idle_scatter_buf_size,
(uint32_t)(scatter_buf_ptr -
(uint8_t *)(soc->wbm_idle_scatter_buf_base_vaddr[
scatter_buf_num-1])), total_link_descs);
}
return 0;
fail:
if (soc->wbm_idle_link_ring.hal_srng) {
dp_srng_cleanup(soc, &soc->wbm_idle_link_ring,
WBM_IDLE_LINK, 0);
}
for (i = 0; i < MAX_IDLE_SCATTER_BUFS; i++) {
if (soc->wbm_idle_scatter_buf_base_vaddr[i]) {
qdf_mem_free_consistent(soc->osdev, soc->osdev->dev,
soc->wbm_idle_scatter_buf_size,
soc->wbm_idle_scatter_buf_base_vaddr[i],
soc->wbm_idle_scatter_buf_base_paddr[i], 0);
soc->wbm_idle_scatter_buf_base_vaddr[i] = NULL;
}
}
for (i = 0; i < MAX_LINK_DESC_BANKS; i++) {
if (soc->link_desc_banks[i].base_vaddr_unaligned) {
qdf_mem_free_consistent(soc->osdev, soc->osdev->dev,
soc->link_desc_banks[i].size,
soc->link_desc_banks[i].base_vaddr_unaligned,
soc->link_desc_banks[i].base_paddr_unaligned,
0);
soc->link_desc_banks[i].base_vaddr_unaligned = NULL;
}
}
return QDF_STATUS_E_FAILURE;
}
/*
* Free link descriptor pool that was setup HW
*/
static void dp_hw_link_desc_pool_cleanup(struct dp_soc *soc)
{
int i;
if (soc->wbm_idle_link_ring.hal_srng) {
dp_srng_cleanup(soc, &soc->wbm_idle_link_ring,
WBM_IDLE_LINK, 0);
}
for (i = 0; i < MAX_IDLE_SCATTER_BUFS; i++) {
if (soc->wbm_idle_scatter_buf_base_vaddr[i]) {
qdf_mem_free_consistent(soc->osdev, soc->osdev->dev,
soc->wbm_idle_scatter_buf_size,
soc->wbm_idle_scatter_buf_base_vaddr[i],
soc->wbm_idle_scatter_buf_base_paddr[i], 0);
soc->wbm_idle_scatter_buf_base_vaddr[i] = NULL;
}
}
for (i = 0; i < MAX_LINK_DESC_BANKS; i++) {
if (soc->link_desc_banks[i].base_vaddr_unaligned) {
qdf_mem_free_consistent(soc->osdev, soc->osdev->dev,
soc->link_desc_banks[i].size,
soc->link_desc_banks[i].base_vaddr_unaligned,
soc->link_desc_banks[i].base_paddr_unaligned,
0);
soc->link_desc_banks[i].base_vaddr_unaligned = NULL;
}
}
}
#ifdef IPA_OFFLOAD
#define REO_DST_RING_SIZE_QCA6290 1023
#ifndef QCA_WIFI_QCA8074_VP
#define REO_DST_RING_SIZE_QCA8074 1023
#else
#define REO_DST_RING_SIZE_QCA8074 8
#endif /* QCA_WIFI_QCA8074_VP */
#else
#define REO_DST_RING_SIZE_QCA6290 1024
#ifndef QCA_WIFI_QCA8074_VP
#define REO_DST_RING_SIZE_QCA8074 2048
#else
#define REO_DST_RING_SIZE_QCA8074 8
#endif /* QCA_WIFI_QCA8074_VP */
#endif /* IPA_OFFLOAD */
#ifndef FEATURE_WDS
static void dp_soc_wds_attach(struct dp_soc *soc)
{
}
static void dp_soc_wds_detach(struct dp_soc *soc)
{
}
#endif
/*
* dp_soc_reset_ring_map() - Reset cpu ring map
* @soc: Datapath soc handler
*
* This api resets the default cpu ring map
*/
static void dp_soc_reset_cpu_ring_map(struct dp_soc *soc)
{
uint8_t i;
int nss_config = wlan_cfg_get_dp_soc_nss_cfg(soc->wlan_cfg_ctx);
for (i = 0; i < WLAN_CFG_INT_NUM_CONTEXTS; i++) {
switch (nss_config) {
case dp_nss_cfg_first_radio:
/*
* Setting Tx ring map for one nss offloaded radio
*/
soc->tx_ring_map[i] = dp_cpu_ring_map[DP_NSS_FIRST_RADIO_OFFLOADED_MAP][i];
break;
case dp_nss_cfg_second_radio:
/*
* Setting Tx ring for two nss offloaded radios
*/
soc->tx_ring_map[i] = dp_cpu_ring_map[DP_NSS_SECOND_RADIO_OFFLOADED_MAP][i];
break;
case dp_nss_cfg_dbdc:
/*
* Setting Tx ring map for 2 nss offloaded radios
*/
soc->tx_ring_map[i] =
dp_cpu_ring_map[DP_NSS_DBDC_OFFLOADED_MAP][i];
break;
case dp_nss_cfg_dbtc:
/*
* Setting Tx ring map for 3 nss offloaded radios
*/
soc->tx_ring_map[i] =
dp_cpu_ring_map[DP_NSS_DBTC_OFFLOADED_MAP][i];
break;
default:
dp_err("tx_ring_map failed due to invalid nss cfg");
break;
}
}
}
/*
* dp_soc_ring_if_nss_offloaded() - find if ring is offloaded to NSS
* @dp_soc - DP soc handle
* @ring_type - ring type
* @ring_num - ring_num
*
* return 0 or 1
*/
static uint8_t dp_soc_ring_if_nss_offloaded(struct dp_soc *soc, enum hal_ring_type ring_type, int ring_num)
{
uint8_t nss_config = wlan_cfg_get_dp_soc_nss_cfg(soc->wlan_cfg_ctx);
uint8_t status = 0;
switch (ring_type) {
case WBM2SW_RELEASE:
case REO_DST:
case RXDMA_BUF:
status = ((nss_config) & (1 << ring_num));
break;
default:
break;
}
return status;
}
/*
* dp_soc_reset_intr_mask() - reset interrupt mask
* @dp_soc - DP Soc handle
*
* Return: Return void
*/
static void dp_soc_reset_intr_mask(struct dp_soc *soc)
{
uint8_t j;
int *grp_mask = NULL;
int group_number, mask, num_ring;
/* number of tx ring */
num_ring = wlan_cfg_num_tcl_data_rings(soc->wlan_cfg_ctx);
/*
* group mask for tx completion ring.
*/
grp_mask = &soc->wlan_cfg_ctx->int_tx_ring_mask[0];
/* loop and reset the mask for only offloaded ring */
for (j = 0; j < num_ring; j++) {
if (!dp_soc_ring_if_nss_offloaded(soc, WBM2SW_RELEASE, j)) {
continue;
}
/*
* Group number corresponding to tx offloaded ring.
*/
group_number = dp_srng_find_ring_in_mask(j, grp_mask);
if (group_number < 0) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
FL("ring not part of any group; ring_type: %d,ring_num %d"),
WBM2SW_RELEASE, j);
return;
}
/* reset the tx mask for offloaded ring */
mask = wlan_cfg_get_tx_ring_mask(soc->wlan_cfg_ctx, group_number);
mask &= (~(1 << j));
/*
* reset the interrupt mask for offloaded ring.
*/
wlan_cfg_set_tx_ring_mask(soc->wlan_cfg_ctx, group_number, mask);
}
/* number of rx rings */
num_ring = wlan_cfg_num_reo_dest_rings(soc->wlan_cfg_ctx);
/*
* group mask for reo destination ring.
*/
grp_mask = &soc->wlan_cfg_ctx->int_rx_ring_mask[0];
/* loop and reset the mask for only offloaded ring */
for (j = 0; j < num_ring; j++) {
if (!dp_soc_ring_if_nss_offloaded(soc, REO_DST, j)) {
continue;
}
/*
* Group number corresponding to rx offloaded ring.
*/
group_number = dp_srng_find_ring_in_mask(j, grp_mask);
if (group_number < 0) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
FL("ring not part of any group; ring_type: %d,ring_num %d"),
REO_DST, j);
return;
}
/* set the interrupt mask for offloaded ring */
mask = wlan_cfg_get_rx_ring_mask(soc->wlan_cfg_ctx, group_number);
mask &= (~(1 << j));
/*
* set the interrupt mask to zero for rx offloaded radio.
*/
wlan_cfg_set_rx_ring_mask(soc->wlan_cfg_ctx, group_number, mask);
}
/*
* group mask for Rx buffer refill ring
*/
grp_mask = &soc->wlan_cfg_ctx->int_host2rxdma_ring_mask[0];
/* loop and reset the mask for only offloaded ring */
for (j = 0; j < MAX_PDEV_CNT; j++) {
if (!dp_soc_ring_if_nss_offloaded(soc, RXDMA_BUF, j)) {
continue;
}
/*
* Group number corresponding to rx offloaded ring.
*/
group_number = dp_srng_find_ring_in_mask(j, grp_mask);
if (group_number < 0) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
FL("ring not part of any group; ring_type: %d,ring_num %d"),
REO_DST, j);
return;
}
/* set the interrupt mask for offloaded ring */
mask = wlan_cfg_get_host2rxdma_ring_mask(soc->wlan_cfg_ctx,
group_number);
mask &= (~(1 << j));
/*
* set the interrupt mask to zero for rx offloaded radio.
*/
wlan_cfg_set_host2rxdma_ring_mask(soc->wlan_cfg_ctx,
group_number, mask);
}
}
#ifdef IPA_OFFLOAD
/**
* dp_reo_remap_config() - configure reo remap register value based
* nss configuration.
* based on offload_radio value below remap configuration
* get applied.
* 0 - both Radios handled by host (remap rings 1, 2, 3 & 4)
* 1 - 1st Radio handled by NSS (remap rings 2, 3 & 4)
* 2 - 2nd Radio handled by NSS (remap rings 1, 2 & 4)
* 3 - both Radios handled by NSS (remap not required)
* 4 - IPA OFFLOAD enabled (remap rings 1,2 & 3)
*
* @remap1: output parameter indicates reo remap 1 register value
* @remap2: output parameter indicates reo remap 2 register value
* Return: bool type, true if remap is configured else false.
*/
bool dp_reo_remap_config(struct dp_soc *soc, uint32_t *remap1, uint32_t *remap2)
{
*remap1 = ((0x1 << 0) | (0x2 << 3) | (0x3 << 6) | (0x1 << 9) |
(0x2 << 12) | (0x3 << 15) | (0x1 << 18) | (0x2 << 21)) << 8;
*remap2 = ((0x3 << 0) | (0x1 << 3) | (0x2 << 6) | (0x3 << 9) |
(0x1 << 12) | (0x2 << 15) | (0x3 << 18) | (0x1 << 21)) << 8;
dp_debug("remap1 %x remap2 %x", *remap1, *remap2);
return true;
}
#else
static bool dp_reo_remap_config(struct dp_soc *soc,
uint32_t *remap1,
uint32_t *remap2)
{
uint8_t offload_radio = wlan_cfg_get_dp_soc_nss_cfg(soc->wlan_cfg_ctx);
switch (offload_radio) {
case dp_nss_cfg_default:
*remap1 = ((0x1 << 0) | (0x2 << 3) | (0x3 << 6) |
(0x4 << 9) | (0x1 << 12) | (0x2 << 15) |
(0x3 << 18) | (0x4 << 21)) << 8;
*remap2 = ((0x1 << 0) | (0x2 << 3) | (0x3 << 6) |
(0x4 << 9) | (0x1 << 12) | (0x2 << 15) |
(0x3 << 18) | (0x4 << 21)) << 8;
break;
case dp_nss_cfg_first_radio:
*remap1 = ((0x2 << 0) | (0x3 << 3) | (0x4 << 6) |
(0x2 << 9) | (0x3 << 12) | (0x4 << 15) |
(0x2 << 18) | (0x3 << 21)) << 8;
*remap2 = ((0x4 << 0) | (0x2 << 3) | (0x3 << 6) |
(0x4 << 9) | (0x2 << 12) | (0x3 << 15) |
(0x4 << 18) | (0x2 << 21)) << 8;
break;
case dp_nss_cfg_second_radio:
*remap1 = ((0x1 << 0) | (0x3 << 3) | (0x4 << 6) |
(0x1 << 9) | (0x3 << 12) | (0x4 << 15) |
(0x1 << 18) | (0x3 << 21)) << 8;
*remap2 = ((0x4 << 0) | (0x1 << 3) | (0x3 << 6) |
(0x4 << 9) | (0x1 << 12) | (0x3 << 15) |
(0x4 << 18) | (0x1 << 21)) << 8;
break;
case dp_nss_cfg_dbdc:
case dp_nss_cfg_dbtc:
/* return false if both or all are offloaded to NSS */
return false;
}
dp_debug("remap1 %x remap2 %x offload_radio %u",
*remap1, *remap2, offload_radio);
return true;
}
#endif
/*
* dp_reo_frag_dst_set() - configure reo register to set the
* fragment destination ring
* @soc : Datapath soc
* @frag_dst_ring : output parameter to set fragment destination ring
*
* Based on offload_radio below fragment destination rings is selected
* 0 - TCL
* 1 - SW1
* 2 - SW2
* 3 - SW3
* 4 - SW4
* 5 - Release
* 6 - FW
* 7 - alternate select
*
* return: void
*/
static void dp_reo_frag_dst_set(struct dp_soc *soc, uint8_t *frag_dst_ring)
{
uint8_t offload_radio = wlan_cfg_get_dp_soc_nss_cfg(soc->wlan_cfg_ctx);
switch (offload_radio) {
case dp_nss_cfg_default:
*frag_dst_ring = REO_REMAP_TCL;
break;
case dp_nss_cfg_dbdc:
case dp_nss_cfg_dbtc:
*frag_dst_ring = HAL_SRNG_REO_ALTERNATE_SELECT;
break;
default:
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
FL("dp_reo_frag_dst_set invalid offload radio config"));
break;
}
}
#ifdef ENABLE_VERBOSE_DEBUG
static void dp_enable_verbose_debug(struct dp_soc *soc)
{
struct wlan_cfg_dp_soc_ctxt *soc_cfg_ctx;
soc_cfg_ctx = soc->wlan_cfg_ctx;
if (soc_cfg_ctx->per_pkt_trace & dp_verbose_debug_mask)
is_dp_verbose_debug_enabled = true;
if (soc_cfg_ctx->per_pkt_trace & hal_verbose_debug_mask)
hal_set_verbose_debug(true);
else
hal_set_verbose_debug(false);
}
#else
static void dp_enable_verbose_debug(struct dp_soc *soc)
{
}
#endif
#ifdef WLAN_FEATURE_STATS_EXT
static inline void dp_create_ext_stats_event(struct dp_soc *soc)
{
qdf_event_create(&soc->rx_hw_stats_event);
}
#else
static inline void dp_create_ext_stats_event(struct dp_soc *soc)
{
}
#endif
/*
* dp_soc_cmn_setup() - Common SoC level initializion
* @soc: Datapath SOC handle
*
* This is an internal function used to setup common SOC data structures,
* to be called from PDEV attach after receiving HW mode capabilities from FW
*/
static int dp_soc_cmn_setup(struct dp_soc *soc)
{
int i;
struct hal_reo_params reo_params;
int tx_ring_size;
int tx_comp_ring_size;
int reo_dst_ring_size;
uint32_t entries;
struct wlan_cfg_dp_soc_ctxt *soc_cfg_ctx;
if (qdf_atomic_read(&soc->cmn_init_done))
return 0;
if (dp_hw_link_desc_pool_setup(soc))
goto fail1;
soc_cfg_ctx = soc->wlan_cfg_ctx;
dp_enable_verbose_debug(soc);
/* Setup SRNG rings */
/* Common rings */
entries = wlan_cfg_get_dp_soc_wbm_release_ring_size(soc_cfg_ctx);
if (dp_srng_setup(soc, &soc->wbm_desc_rel_ring, SW2WBM_RELEASE, 0, 0,
entries, 0)) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
FL("dp_srng_setup failed for wbm_desc_rel_ring"));
goto fail1;
}
qdf_minidump_log(
(void *)(soc->wbm_desc_rel_ring.base_vaddr_unaligned),
soc->wbm_desc_rel_ring.alloc_size, "wbm_desc_rel_ring");
soc->num_tcl_data_rings = 0;
/* Tx data rings */
if (!wlan_cfg_per_pdev_tx_ring(soc_cfg_ctx)) {
soc->num_tcl_data_rings =
wlan_cfg_num_tcl_data_rings(soc_cfg_ctx);
tx_comp_ring_size =
wlan_cfg_tx_comp_ring_size(soc_cfg_ctx);
tx_ring_size =
wlan_cfg_tx_ring_size(soc_cfg_ctx);
for (i = 0; i < soc->num_tcl_data_rings; i++) {
if (dp_srng_setup(soc, &soc->tcl_data_ring[i],
TCL_DATA, i, 0, tx_ring_size, 0)) {
QDF_TRACE(QDF_MODULE_ID_DP,
QDF_TRACE_LEVEL_ERROR,
FL("dp_srng_setup failed for tcl_data_ring[%d]"), i);
goto fail1;
}
/*
* TBD: Set IPA WBM ring size with ini IPA UC tx buffer
* count
*/
if (dp_srng_setup(soc, &soc->tx_comp_ring[i],
WBM2SW_RELEASE, i, 0,
tx_comp_ring_size,
WLAN_CFG_DST_RING_CACHED_DESC)) {
QDF_TRACE(QDF_MODULE_ID_DP,
QDF_TRACE_LEVEL_ERROR,
FL("dp_srng_setup failed for tx_comp_ring[%d]"), i);
goto fail1;
}
}
} else {
/* This will be incremented during per pdev ring setup */
soc->num_tcl_data_rings = 0;
}
if (dp_tx_soc_attach(soc)) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
FL("dp_tx_soc_attach failed"));
goto fail1;
}
entries = wlan_cfg_get_dp_soc_tcl_cmd_ring_size(soc_cfg_ctx);
/* TCL command and status rings */
if (dp_srng_setup(soc, &soc->tcl_cmd_ring, TCL_CMD, 0, 0,
entries, 0)) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
FL("dp_srng_setup failed for tcl_cmd_ring"));
goto fail1;
}
entries = wlan_cfg_get_dp_soc_tcl_status_ring_size(soc_cfg_ctx);
if (dp_srng_setup(soc, &soc->tcl_status_ring, TCL_STATUS, 0, 0,
entries, 0)) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
FL("dp_srng_setup failed for tcl_status_ring"));
goto fail1;
}
reo_dst_ring_size = wlan_cfg_get_reo_dst_ring_size(soc->wlan_cfg_ctx);
/* TBD: call dp_tx_init to setup Tx SW descriptors and MSDU extension
* descriptors
*/
/* Rx data rings */
if (!wlan_cfg_per_pdev_rx_ring(soc_cfg_ctx)) {
soc->num_reo_dest_rings =
wlan_cfg_num_reo_dest_rings(soc_cfg_ctx);
QDF_TRACE(QDF_MODULE_ID_DP,
QDF_TRACE_LEVEL_INFO,
FL("num_reo_dest_rings %d"), soc->num_reo_dest_rings);
for (i = 0; i < soc->num_reo_dest_rings; i++) {
if (dp_srng_setup(soc, &soc->reo_dest_ring[i], REO_DST,
i, 0, reo_dst_ring_size,
WLAN_CFG_DST_RING_CACHED_DESC)) {
QDF_TRACE(QDF_MODULE_ID_DP,
QDF_TRACE_LEVEL_ERROR,
FL(RNG_ERR "reo_dest_ring [%d]"), i);
goto fail1;
}
}
} else {
/* This will be incremented during per pdev ring setup */
soc->num_reo_dest_rings = 0;
}
entries = wlan_cfg_get_dp_soc_rxdma_err_dst_ring_size(soc_cfg_ctx);
/* LMAC RxDMA to SW Rings configuration */
if (!wlan_cfg_per_pdev_lmac_ring(soc_cfg_ctx)) {
/* Only valid for MCL */
struct dp_pdev *pdev = soc->pdev_list[0];
for (i = 0; i < MAX_RX_MAC_RINGS; i++) {
if (dp_srng_setup(soc, &pdev->rxdma_err_dst_ring[i],
RXDMA_DST, 0, i, entries, 0)) {
QDF_TRACE(QDF_MODULE_ID_DP,
QDF_TRACE_LEVEL_ERROR,
FL(RNG_ERR "rxdma_err_dst_ring"));
goto fail1;
}
}
}
/* TBD: call dp_rx_init to setup Rx SW descriptors */
/* REO reinjection ring */
entries = wlan_cfg_get_dp_soc_reo_reinject_ring_size(soc_cfg_ctx);
if (dp_srng_setup(soc, &soc->reo_reinject_ring, REO_REINJECT, 0, 0,
entries, 0)) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
FL("dp_srng_setup failed for reo_reinject_ring"));
goto fail1;
}
/* Rx release ring */
if (dp_srng_setup(soc, &soc->rx_rel_ring, WBM2SW_RELEASE, 3, 0,
wlan_cfg_get_dp_soc_rx_release_ring_size(soc_cfg_ctx),
0)) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
FL("dp_srng_setup failed for rx_rel_ring"));
goto fail1;
}
/* Rx exception ring */
entries = wlan_cfg_get_dp_soc_reo_exception_ring_size(soc_cfg_ctx);
if (dp_srng_setup(soc, &soc->reo_exception_ring,
REO_EXCEPTION, 0, MAX_REO_DEST_RINGS, entries, 0)) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
FL("dp_srng_setup failed for reo_exception_ring"));
goto fail1;
}
/* REO command and status rings */
if (dp_srng_setup(soc, &soc->reo_cmd_ring, REO_CMD, 0, 0,
wlan_cfg_get_dp_soc_reo_cmd_ring_size(soc_cfg_ctx),
0)) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
FL("dp_srng_setup failed for reo_cmd_ring"));
goto fail1;
}
hal_reo_init_cmd_ring(soc->hal_soc, soc->reo_cmd_ring.hal_srng);
TAILQ_INIT(&soc->rx.reo_cmd_list);
qdf_spinlock_create(&soc->rx.reo_cmd_lock);
if (dp_srng_setup(soc, &soc->reo_status_ring, REO_STATUS, 0, 0,
wlan_cfg_get_dp_soc_reo_status_ring_size(soc_cfg_ctx),
0)) {
QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
FL("dp_srng_setup failed for reo_status_ring"));
goto fail1;
}
/* Reset the cpu ring map if radio is NSS offloaded */
if (wlan_cfg_get_dp_soc_nss_cfg(soc_cfg_ctx)) {
dp_soc_reset_cpu_ring_map(soc);
dp_soc_reset_intr_mask(soc);
}
/* Setup HW REO */
qdf_mem_zero(&reo_params, sizeof(reo_params));
if (wlan_cfg_is_rx_hash_enabled(soc_cfg_ctx)) {
/*
* Reo ring remap is not required if both radios
* are offloaded to NSS
*/
if (!dp_reo_remap_config(soc,
&reo_params.remap1,
&reo_params.remap2))
goto out;
reo_params.rx_hash_enabled = true;
}
/* setup the global rx defrag waitlist */
TAILQ_INIT(&soc->rx.defrag.waitlist);
soc->rx.defrag.timeout_ms =
wlan_cfg_get_rx_defrag_min_timeout(soc_cfg_ctx);
soc->rx.defrag.next_flush_ms = 0;
soc->rx.flags.defrag_timeout_check =
wlan_cfg_get_defrag_timeout_check(soc_cfg_ctx);
qdf_spinlock_create(&soc->rx.defrag.defrag_lock);
dp_create_ext_stats_event(soc);
out:
/*
* set the fragment destination ring
*/
dp_reo_frag_dst_set(soc, &reo_params.frag_dst_ring);
hal_reo_setup(soc->hal_soc, &reo_params);
hal_reo_set_err_dst_remap(soc->hal_soc);
qdf_atomic_set(&soc->cmn_init_done, 1);
dp_soc_wds_attach(soc);
qdf_nbuf_queue_init(&soc->htt_stats.msg);
return 0;
fail1:
/*
* Cleanup will be done as part of soc_detach, which will
* be called on pdev attach failure
*/
return QDF_STATUS_E_FAILURE;
}
/*
* dp_soc_cmn_cleanup() - Common SoC level De-initializion
*
* @soc: Datapath SOC handle
*
* This function is responsible for cleaning up DP resource of Soc
* initialled in dp_pdev_attach_wifi3-->dp_soc_cmn_setup, since
* dp_soc_detach_wifi3 could not identify some of them
* whether they have done initialized or not accurately.
*
*/
static void dp_soc_cmn_cleanup(struct dp_soc *soc)
{
dp_tx_soc_detach(soc);