qcacld-3.0: Featurize Tx throttle feature

Featurize Tx throttle feature.

Change-Id: Ie97749367b8c4e7af7fe0bee68b15be5946fdd67
CRs-Fixed: 2257918
This commit is contained in:
Nirav Shah 2018-04-21 12:49:28 +05:30 • committed by nshrivas
commit 46fc6301ea
5 changed files with 337 additions and 290 deletions

4
Kbuild
View file

@ -898,6 +898,10 @@ else
TXRX_OBJS += $(TXRX_DIR)/ol_tx_ll_legacy.o
endif
ifeq ($(CONFIG_QCA_SUPPORT_TX_THROTTLE), y)
TXRX_OBJS += $(TXRX_DIR)/ol_tx_throttle.o
endif
ifeq ($(CONFIG_LITHIUM), y)
############ DP 3.0 ############
DP_INC := -I$(WLAN_COMMON_ROOT)/dp/inc \

View file

@ -154,6 +154,22 @@ void ol_txrx_tx_release(ol_txrx_peer_handle peer,
u_int32_t tid_mask,
int max_frms);
#else
static inline void
ol_txrx_peer_tid_unpause(ol_txrx_peer_handle data_peer, int tid)
{
}
static inline void
ol_txrx_tx_release(ol_txrx_peer_handle peer,
u_int32_t tid_mask,
int max_frms)
{
}
#endif /* CONFIG_HL_SUPPORT */
#ifdef QCA_SUPPORT_TX_THROTTLE
/**
* @brief Suspend all tx data per thermal event/timer for the
* specified physical device
@ -174,19 +190,7 @@ ol_txrx_throttle_pause(ol_txrx_pdev_handle data_pdev);
*/
void
ol_txrx_throttle_unpause(ol_txrx_pdev_handle data_pdev);
#else
static inline void
ol_txrx_peer_tid_unpause(ol_txrx_peer_handle data_peer, int tid)
{
}
static inline void
ol_txrx_tx_release(ol_txrx_peer_handle peer,
u_int32_t tid_mask,
int max_frms)
{
}
static inline void
ol_txrx_throttle_pause(ol_txrx_pdev_handle data_pdev)
@ -197,8 +201,7 @@ static inline void
ol_txrx_throttle_unpause(ol_txrx_pdev_handle data_pdev)
{
}
#endif /* CONFIG_HL_SUPPORT */
#endif
/**
* @brief notify tx data SW that a peer's transmissions are suspended.

View file

@ -537,40 +537,6 @@ ol_txrx_peer_tid_unpause(ol_txrx_peer_handle peer, int tid)
TX_SCHED_DEBUG_PRINT("Leave %s\n", __func__);
}
void
ol_txrx_throttle_pause(ol_txrx_pdev_handle pdev)
{
#if defined(QCA_SUPPORT_TX_THROTTLE)
qdf_spin_lock_bh(&pdev->tx_throttle.mutex);
if (pdev->tx_throttle.is_paused == true) {
qdf_spin_unlock_bh(&pdev->tx_throttle.mutex);
return;
}
pdev->tx_throttle.is_paused = true;
qdf_spin_unlock_bh(&pdev->tx_throttle.mutex);
#endif
ol_txrx_pdev_pause(pdev, 0);
}
void
ol_txrx_throttle_unpause(ol_txrx_pdev_handle pdev)
{
#if defined(QCA_SUPPORT_TX_THROTTLE)
qdf_spin_lock_bh(&pdev->tx_throttle.mutex);
if (pdev->tx_throttle.is_paused == false) {
qdf_spin_unlock_bh(&pdev->tx_throttle.mutex);
return;
}
pdev->tx_throttle.is_paused = false;
qdf_spin_unlock_bh(&pdev->tx_throttle.mutex);
#endif
ol_txrx_pdev_unpause(pdev, 0);
}
void
ol_txrx_vdev_pause(struct cdp_vdev *pvdev, uint32_t reason)
{
@ -1735,248 +1701,6 @@ void ol_txrx_pdev_unpause(struct ol_txrx_pdev_t *pdev, uint32_t reason)
}
#endif
/*--- LL tx throttle queue code --------------------------------------------*/
#if defined(QCA_SUPPORT_TX_THROTTLE)
#ifdef QCA_LL_TX_FLOW_CONTROL_V2
/**
* ol_txrx_thermal_pause() - pause due to thermal mitigation
* @pdev: pdev handle
*
* Return: none
*/
static inline
void ol_txrx_thermal_pause(struct ol_txrx_pdev_t *pdev)
{
ol_txrx_pdev_pause(pdev, OL_TXQ_PAUSE_REASON_THERMAL_MITIGATION);
}
/**
* ol_txrx_thermal_unpause() - unpause due to thermal mitigation
* @pdev: pdev handle
*
* Return: none
*/
static inline
void ol_txrx_thermal_unpause(struct ol_txrx_pdev_t *pdev)
{
ol_txrx_pdev_unpause(pdev, OL_TXQ_PAUSE_REASON_THERMAL_MITIGATION);
}
#else
/**
* ol_txrx_thermal_pause() - pause due to thermal mitigation
* @pdev: pdev handle
*
* Return: none
*/
static inline
void ol_txrx_thermal_pause(struct ol_txrx_pdev_t *pdev)
{
}
/**
* ol_txrx_thermal_unpause() - unpause due to thermal mitigation
* @pdev: pdev handle
*
* Return: none
*/
static inline
void ol_txrx_thermal_unpause(struct ol_txrx_pdev_t *pdev)
{
ol_tx_pdev_ll_pause_queue_send_all(pdev);
}
#endif
static void ol_tx_pdev_throttle_phase_timer(void *context)
{
struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)context;
int ms;
enum throttle_level cur_level;
enum throttle_phase cur_phase;
/* update the phase */
pdev->tx_throttle.current_throttle_phase++;
if (pdev->tx_throttle.current_throttle_phase == THROTTLE_PHASE_MAX)
pdev->tx_throttle.current_throttle_phase = THROTTLE_PHASE_OFF;
if (pdev->tx_throttle.current_throttle_phase == THROTTLE_PHASE_OFF) {
/* Traffic is stopped */
ol_txrx_dbg(
"throttle phase --> OFF\n");
ol_txrx_throttle_pause(pdev);
ol_txrx_thermal_pause(pdev);
cur_level = pdev->tx_throttle.current_throttle_level;
cur_phase = pdev->tx_throttle.current_throttle_phase;
ms = pdev->tx_throttle.throttle_time_ms[cur_level][cur_phase];
if (pdev->tx_throttle.current_throttle_level !=
THROTTLE_LEVEL_0) {
ol_txrx_dbg(
"start timer %d ms\n", ms);
qdf_timer_start(&pdev->tx_throttle.
phase_timer, ms);
}
} else {
/* Traffic can go */
ol_txrx_dbg(
"throttle phase --> ON\n");
ol_txrx_throttle_unpause(pdev);
ol_txrx_thermal_unpause(pdev);
cur_level = pdev->tx_throttle.current_throttle_level;
cur_phase = pdev->tx_throttle.current_throttle_phase;
ms = pdev->tx_throttle.throttle_time_ms[cur_level][cur_phase];
if (pdev->tx_throttle.current_throttle_level !=
THROTTLE_LEVEL_0) {
ol_txrx_dbg("start timer %d ms\n",
ms);
qdf_timer_start(&pdev->tx_throttle.phase_timer,
ms);
}
}
}
#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL
static void ol_tx_pdev_throttle_tx_timer(void *context)
{
struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)context;
ol_tx_pdev_ll_pause_queue_send_all(pdev);
}
#endif
#ifdef CONFIG_HL_SUPPORT
/**
* ol_tx_set_throttle_phase_time() - Set the thermal mitgation throttle phase
* and time
* @pdev: the peer device object
* @level: throttle phase level
* @ms: throttle time
*
* Return: None
*/
static void
ol_tx_set_throttle_phase_time(struct ol_txrx_pdev_t *pdev, int level, int *ms)
{
qdf_timer_stop(&pdev->tx_throttle.phase_timer);
/* Set the phase */
if (level != THROTTLE_LEVEL_0) {
pdev->tx_throttle.current_throttle_phase = THROTTLE_PHASE_OFF;
*ms = pdev->tx_throttle.throttle_time_ms[level]
[THROTTLE_PHASE_OFF];
/* pause all */
ol_txrx_throttle_pause(pdev);
} else {
pdev->tx_throttle.current_throttle_phase = THROTTLE_PHASE_ON;
*ms = pdev->tx_throttle.throttle_time_ms[level]
[THROTTLE_PHASE_ON];
/* unpause all */
ol_txrx_throttle_unpause(pdev);
}
}
#else
static void
ol_tx_set_throttle_phase_time(struct ol_txrx_pdev_t *pdev, int level, int *ms)
{
/* Reset the phase */
pdev->tx_throttle.current_throttle_phase = THROTTLE_PHASE_OFF;
/* Start with the new time */
*ms = pdev->tx_throttle.
throttle_time_ms[level][THROTTLE_PHASE_OFF];
qdf_timer_stop(&pdev->tx_throttle.phase_timer);
}
#endif
void ol_tx_throttle_set_level(struct cdp_pdev *ppdev, int level)
{
struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev;
int ms = 0;
if (level >= THROTTLE_LEVEL_MAX) {
ol_txrx_dbg(
"%s invalid throttle level set %d, ignoring\n",
__func__, level);
return;
}
ol_txrx_info("Setting throttle level %d\n", level);
/* Set the current throttle level */
pdev->tx_throttle.current_throttle_level = (enum throttle_level) level;
ol_tx_set_throttle_phase_time(pdev, level, &ms);
if (level != THROTTLE_LEVEL_0)
qdf_timer_start(&pdev->tx_throttle.phase_timer, ms);
}
void ol_tx_throttle_init_period(struct cdp_pdev *ppdev, int period,
uint8_t *dutycycle_level)
{
struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev;
int i;
/* Set the current throttle level */
pdev->tx_throttle.throttle_period_ms = period;
ol_txrx_dbg("level OFF ON\n");
for (i = 0; i < THROTTLE_LEVEL_MAX; i++) {
pdev->tx_throttle.throttle_time_ms[i][THROTTLE_PHASE_ON] =
pdev->tx_throttle.throttle_period_ms -
((dutycycle_level[i] *
pdev->tx_throttle.throttle_period_ms)/100);
pdev->tx_throttle.throttle_time_ms[i][THROTTLE_PHASE_OFF] =
pdev->tx_throttle.throttle_period_ms -
pdev->tx_throttle.throttle_time_ms[
i][THROTTLE_PHASE_ON];
ol_txrx_dbg("%d %d %d\n", i,
pdev->tx_throttle.
throttle_time_ms[i][THROTTLE_PHASE_OFF],
pdev->tx_throttle.
throttle_time_ms[i][THROTTLE_PHASE_ON]);
}
}
void ol_tx_throttle_init(struct ol_txrx_pdev_t *pdev)
{
uint32_t throttle_period;
uint8_t dutycycle_level[THROTTLE_LEVEL_MAX];
int i;
pdev->tx_throttle.current_throttle_level = THROTTLE_LEVEL_0;
pdev->tx_throttle.current_throttle_phase = THROTTLE_PHASE_OFF;
qdf_spinlock_create(&pdev->tx_throttle.mutex);
throttle_period = ol_cfg_throttle_period_ms(pdev->ctrl_pdev);
for (i = 0; i < THROTTLE_LEVEL_MAX; i++)
dutycycle_level[i] =
ol_cfg_throttle_duty_cycle_level(pdev->ctrl_pdev, i);
ol_tx_throttle_init_period((struct cdp_pdev *)pdev,
throttle_period, &dutycycle_level[0]);
qdf_timer_init(pdev->osdev,
&pdev->tx_throttle.phase_timer,
ol_tx_pdev_throttle_phase_timer, pdev,
QDF_TIMER_TYPE_SW);
#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL
qdf_timer_init(pdev->osdev,
&pdev->tx_throttle.tx_timer,
ol_tx_pdev_throttle_tx_timer, pdev,
QDF_TIMER_TYPE_SW);
#endif
pdev->tx_throttle.tx_threshold = THROTTLE_TX_THRESHOLD;
}
#endif /* QCA_SUPPORT_TX_THROTTLE */
#ifdef FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL
/**

View file

@ -445,6 +445,14 @@ void ol_tx_throttle_init_period(struct cdp_pdev *ppdev, int period,
void ol_tx_throttle_init(struct ol_txrx_pdev_t *pdev);
#else
static inline void ol_tx_throttle_init(struct ol_txrx_pdev_t *pdev) {}
static inline void ol_tx_throttle_set_level(struct cdp_pdev *ppdev, int level)
{}
static inline void ol_tx_throttle_init_period(struct cdp_pdev *ppdev,
int period,
uint8_t *dutycycle_level)
{}
#endif
#ifdef FEATURE_HL_GROUP_CREDIT_FLOW_CONTROL

View file

@ -0,0 +1,308 @@
/*
* Copyright (c) 2012-2018 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.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
#include <qdf_nbuf.h> /* qdf_nbuf_t, etc. */
#include <qdf_atomic.h> /* qdf_atomic_read, etc. */
#include <ol_cfg.h> /* ol_cfg_addba_retry */
#include <htt.h> /* HTT_TX_EXT_TID_MGMT */
#include <ol_htt_tx_api.h> /* htt_tx_desc_tid */
#include <ol_txrx_api.h> /* ol_txrx_vdev_handle */
#include <ol_txrx_ctrl_api.h> /* ol_txrx_sync, ol_tx_addba_conf */
#include <cdp_txrx_tx_throttle.h>
#include <ol_ctrl_txrx_api.h> /* ol_ctrl_addba_req */
#include <ol_txrx_internal.h> /* TXRX_ASSERT1, etc. */
#include <ol_tx_desc.h> /* ol_tx_desc, ol_tx_desc_frame_list_free */
#include <ol_tx.h> /* ol_tx_vdev_ll_pause_queue_send */
#include <ol_tx_sched.h> /* ol_tx_sched_notify, etc. */
#include <ol_tx_queue.h>
#include <ol_txrx.h> /* ol_tx_desc_pool_size_hl */
#include <ol_txrx_dbg.h> /* ENABLE_TX_QUEUE_LOG */
#include <qdf_types.h> /* bool */
#include "cdp_txrx_flow_ctrl_legacy.h"
#include <ol_txrx_peer_find.h>
#include <cdp_txrx_handle.h>
#ifdef QCA_LL_TX_FLOW_CONTROL_V2
/**
* ol_txrx_thermal_pause() - pause due to thermal mitigation
* @pdev: pdev handle
*
* Return: none
*/
static inline
void ol_txrx_thermal_pause(struct ol_txrx_pdev_t *pdev)
{
ol_txrx_pdev_pause(pdev, OL_TXQ_PAUSE_REASON_THERMAL_MITIGATION);
}
/**
* ol_txrx_thermal_unpause() - unpause due to thermal mitigation
* @pdev: pdev handle
*
* Return: none
*/
static inline
void ol_txrx_thermal_unpause(struct ol_txrx_pdev_t *pdev)
{
ol_txrx_pdev_unpause(pdev, OL_TXQ_PAUSE_REASON_THERMAL_MITIGATION);
}
#else
/**
* ol_txrx_thermal_pause() - pause due to thermal mitigation
* @pdev: pdev handle
*
* Return: none
*/
static inline
void ol_txrx_thermal_pause(struct ol_txrx_pdev_t *pdev)
{
}
/**
* ol_txrx_thermal_unpause() - unpause due to thermal mitigation
* @pdev: pdev handle
*
* Return: none
*/
static inline
void ol_txrx_thermal_unpause(struct ol_txrx_pdev_t *pdev)
{
ol_tx_pdev_ll_pause_queue_send_all(pdev);
}
#endif
static void ol_tx_pdev_throttle_phase_timer(void *context)
{
struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)context;
int ms;
enum throttle_level cur_level;
enum throttle_phase cur_phase;
/* update the phase */
pdev->tx_throttle.current_throttle_phase++;
if (pdev->tx_throttle.current_throttle_phase == THROTTLE_PHASE_MAX)
pdev->tx_throttle.current_throttle_phase = THROTTLE_PHASE_OFF;
if (pdev->tx_throttle.current_throttle_phase == THROTTLE_PHASE_OFF) {
/* Traffic is stopped */
ol_txrx_dbg(
"throttle phase --> OFF\n");
ol_txrx_throttle_pause(pdev);
ol_txrx_thermal_pause(pdev);
cur_level = pdev->tx_throttle.current_throttle_level;
cur_phase = pdev->tx_throttle.current_throttle_phase;
ms = pdev->tx_throttle.throttle_time_ms[cur_level][cur_phase];
if (pdev->tx_throttle.current_throttle_level !=
THROTTLE_LEVEL_0) {
ol_txrx_dbg(
"start timer %d ms\n", ms);
qdf_timer_start(&pdev->tx_throttle.
phase_timer, ms);
}
} else {
/* Traffic can go */
ol_txrx_dbg(
"throttle phase --> ON\n");
ol_txrx_throttle_unpause(pdev);
ol_txrx_thermal_unpause(pdev);
cur_level = pdev->tx_throttle.current_throttle_level;
cur_phase = pdev->tx_throttle.current_throttle_phase;
ms = pdev->tx_throttle.throttle_time_ms[cur_level][cur_phase];
if (pdev->tx_throttle.current_throttle_level !=
THROTTLE_LEVEL_0) {
ol_txrx_dbg("start timer %d ms\n", ms);
qdf_timer_start(&pdev->tx_throttle.phase_timer, ms);
}
}
}
#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL
static void ol_tx_pdev_throttle_tx_timer(void *context)
{
struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)context;
ol_tx_pdev_ll_pause_queue_send_all(pdev);
}
#endif
#ifdef CONFIG_HL_SUPPORT
/**
* ol_tx_set_throttle_phase_time() - Set the thermal mitgation throttle phase
* and time
* @pdev: the peer device object
* @level: throttle phase level
* @ms: throttle time
*
* Return: None
*/
static void
ol_tx_set_throttle_phase_time(struct ol_txrx_pdev_t *pdev, int level, int *ms)
{
qdf_timer_stop(&pdev->tx_throttle.phase_timer);
/* Set the phase */
if (level != THROTTLE_LEVEL_0) {
pdev->tx_throttle.current_throttle_phase = THROTTLE_PHASE_OFF;
*ms = pdev->tx_throttle.throttle_time_ms[level]
[THROTTLE_PHASE_OFF];
/* pause all */
ol_txrx_throttle_pause(pdev);
} else {
pdev->tx_throttle.current_throttle_phase = THROTTLE_PHASE_ON;
*ms = pdev->tx_throttle.throttle_time_ms[level]
[THROTTLE_PHASE_ON];
/* unpause all */
ol_txrx_throttle_unpause(pdev);
}
}
#else
static void
ol_tx_set_throttle_phase_time(struct ol_txrx_pdev_t *pdev, int level, int *ms)
{
/* Reset the phase */
pdev->tx_throttle.current_throttle_phase = THROTTLE_PHASE_OFF;
/* Start with the new time */
*ms = pdev->tx_throttle.
throttle_time_ms[level][THROTTLE_PHASE_OFF];
qdf_timer_stop(&pdev->tx_throttle.phase_timer);
}
#endif
void ol_tx_throttle_set_level(struct cdp_pdev *ppdev, int level)
{
struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev;
int ms = 0;
if (level >= THROTTLE_LEVEL_MAX) {
ol_txrx_dbg(
"%s invalid throttle level set %d, ignoring\n",
__func__, level);
return;
}
ol_txrx_info("Setting throttle level %d\n", level);
/* Set the current throttle level */
pdev->tx_throttle.current_throttle_level = (enum throttle_level)level;
ol_tx_set_throttle_phase_time(pdev, level, &ms);
if (level != THROTTLE_LEVEL_0)
qdf_timer_start(&pdev->tx_throttle.phase_timer, ms);
}
void ol_tx_throttle_init_period(struct cdp_pdev *ppdev, int period,
uint8_t *dutycycle_level)
{
struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev;
int i;
/* Set the current throttle level */
pdev->tx_throttle.throttle_period_ms = period;
ol_txrx_dbg("level OFF ON\n");
for (i = 0; i < THROTTLE_LEVEL_MAX; i++) {
pdev->tx_throttle.throttle_time_ms[i][THROTTLE_PHASE_ON] =
pdev->tx_throttle.throttle_period_ms -
((dutycycle_level[i] *
pdev->tx_throttle.throttle_period_ms) / 100);
pdev->tx_throttle.throttle_time_ms[i][THROTTLE_PHASE_OFF] =
pdev->tx_throttle.throttle_period_ms -
pdev->tx_throttle.throttle_time_ms[
i][THROTTLE_PHASE_ON];
ol_txrx_dbg("%d %d %d\n", i,
pdev->tx_throttle.
throttle_time_ms[i][THROTTLE_PHASE_OFF],
pdev->tx_throttle.
throttle_time_ms[i][THROTTLE_PHASE_ON]);
}
}
void ol_tx_throttle_init(struct ol_txrx_pdev_t *pdev)
{
uint32_t throttle_period;
uint8_t dutycycle_level[THROTTLE_LEVEL_MAX];
int i;
pdev->tx_throttle.current_throttle_level = THROTTLE_LEVEL_0;
pdev->tx_throttle.current_throttle_phase = THROTTLE_PHASE_OFF;
qdf_spinlock_create(&pdev->tx_throttle.mutex);
throttle_period = ol_cfg_throttle_period_ms(pdev->ctrl_pdev);
for (i = 0; i < THROTTLE_LEVEL_MAX; i++)
dutycycle_level[i] =
ol_cfg_throttle_duty_cycle_level(pdev->ctrl_pdev, i);
ol_tx_throttle_init_period((struct cdp_pdev *)pdev,
throttle_period, &dutycycle_level[0]);
qdf_timer_init(pdev->osdev, &pdev->tx_throttle.phase_timer,
ol_tx_pdev_throttle_phase_timer, pdev,
QDF_TIMER_TYPE_SW);
#ifdef QCA_LL_LEGACY_TX_FLOW_CONTROL
qdf_timer_init(pdev->osdev, &pdev->tx_throttle.tx_timer,
ol_tx_pdev_throttle_tx_timer, pdev, QDF_TIMER_TYPE_SW);
#endif
pdev->tx_throttle.tx_threshold = THROTTLE_TX_THRESHOLD;
}
void
ol_txrx_throttle_pause(ol_txrx_pdev_handle pdev)
{
qdf_spin_lock_bh(&pdev->tx_throttle.mutex);
if (pdev->tx_throttle.is_paused) {
qdf_spin_unlock_bh(&pdev->tx_throttle.mutex);
return;
}
pdev->tx_throttle.is_paused = true;
qdf_spin_unlock_bh(&pdev->tx_throttle.mutex);
ol_txrx_pdev_pause(pdev, 0);
}
void
ol_txrx_throttle_unpause(ol_txrx_pdev_handle pdev)
{
qdf_spin_lock_bh(&pdev->tx_throttle.mutex);
if (!pdev->tx_throttle.is_paused) {
qdf_spin_unlock_bh(&pdev->tx_throttle.mutex);
return;
}
pdev->tx_throttle.is_paused = false;
qdf_spin_unlock_bh(&pdev->tx_throttle.mutex);
ol_txrx_pdev_unpause(pdev, 0);
}