qcacmn: Force update HP/TP upon delayed register writes
Currently if HP/TP register updates are delayed due to delayed reg write work not getting scheduled, although driver has processed the ring completely, hardware would see HP/TP delta and fires an interrupt based on interrupt threshold configuration until the HP/TP updates reach the hardware. When system is heavily stressed, this delay in HP/TP updates would result in IRQ storm further stressing the system which is bad. Force update HP/TP to the hardware under such scenarios to avoid this problem. Currently doing this just for CE DST SRNGs, this can be scaled to other SRNGs on need. Change-Id: I8a4938dbd4850d7ab6ae5183186237a5e37e1038 CRs-Fixed: 3749078
This commit is contained in:

committed by
Ravindra Konda

parent
e454eb5052
commit
e2e92aa7d6
@@ -877,6 +877,27 @@ static inline int hal_get_reg_write_pending_work(void *hal_soc)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(FEATURE_HAL_DELAYED_REG_WRITE) && defined(QCA_WIFI_QCA6750)
|
||||||
|
/**
|
||||||
|
* hal_srng_check_and_update_hptp() - Check and force update HP/TP
|
||||||
|
* to the hardware
|
||||||
|
* @hal_soc: HAL soc handle
|
||||||
|
* @srng: SRNG handle
|
||||||
|
* @update: Whether or not update is needed
|
||||||
|
*
|
||||||
|
* Returns: void
|
||||||
|
*/
|
||||||
|
void hal_srng_check_and_update_hptp(struct hal_soc *hal_soc,
|
||||||
|
struct hal_srng *srng,
|
||||||
|
bool update);
|
||||||
|
#else
|
||||||
|
static inline void
|
||||||
|
hal_srng_check_and_update_hptp(struct hal_soc *hal_soc, struct hal_srng *srng,
|
||||||
|
bool update)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hal_read_address_32_mb() - Read 32-bit value from the register
|
* hal_read_address_32_mb() - Read 32-bit value from the register
|
||||||
* @soc: soc handle
|
* @soc: soc handle
|
||||||
|
@@ -868,6 +868,12 @@ struct hal_srng {
|
|||||||
|
|
||||||
/* srng specific delayed write stats */
|
/* srng specific delayed write stats */
|
||||||
struct hal_reg_write_srng_stats wstats;
|
struct hal_reg_write_srng_stats wstats;
|
||||||
|
|
||||||
|
union {
|
||||||
|
uint32_t updated_hp;
|
||||||
|
uint32_t updated_tp;
|
||||||
|
};
|
||||||
|
uint32_t force_cnt;
|
||||||
#endif
|
#endif
|
||||||
#ifdef WLAN_DP_SRNG_USAGE_WM_TRACKING
|
#ifdef WLAN_DP_SRNG_USAGE_WM_TRACKING
|
||||||
struct hal_srng_high_wm_info high_wm;
|
struct hal_srng_high_wm_info high_wm;
|
||||||
|
@@ -673,6 +673,72 @@ int hal_get_reg_write_pending_work(void *hal_soc)
|
|||||||
#define HAL_REG_WRITE_QUEUE_LEN 32
|
#define HAL_REG_WRITE_QUEUE_LEN 32
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef QCA_WIFI_QCA6750
|
||||||
|
|
||||||
|
#define HAL_DEL_WRITE_FORCE_UPDATE_THRES 5
|
||||||
|
|
||||||
|
static inline void hal_srng_update_last_hptp(struct hal_srng *srng)
|
||||||
|
{
|
||||||
|
if (srng->ring_dir == HAL_SRNG_SRC_RING)
|
||||||
|
srng->updated_hp = srng->u.src_ring.hp;
|
||||||
|
else
|
||||||
|
srng->updated_tp = srng->u.dst_ring.tp;
|
||||||
|
|
||||||
|
srng->force_cnt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If HP/TP register updates are delayed due to delayed reg
|
||||||
|
* write work not getting scheduled, hardware would see HP/TP
|
||||||
|
* delta and will fire interrupts until the HP/TP updates reach
|
||||||
|
* the hardware.
|
||||||
|
*
|
||||||
|
* When system is heavily stressed, this delay in HP/TP updates
|
||||||
|
* would result in IRQ storm further stressing the system. Force
|
||||||
|
* update HP/TP to the hardware under such scenarios to avoid this.
|
||||||
|
*/
|
||||||
|
void hal_srng_check_and_update_hptp(struct hal_soc *hal_soc,
|
||||||
|
struct hal_srng *srng, bool update)
|
||||||
|
{
|
||||||
|
uint32_t value;
|
||||||
|
|
||||||
|
if (!update)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SRNG_LOCK(&srng->lock);
|
||||||
|
if (srng->ring_dir == HAL_SRNG_SRC_RING) {
|
||||||
|
value = srng->u.src_ring.hp;
|
||||||
|
|
||||||
|
if (value == srng->updated_hp ||
|
||||||
|
srng->force_cnt++ < HAL_DEL_WRITE_FORCE_UPDATE_THRES)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
hal_write_address_32_mb(hal_soc, srng->u.src_ring.hp_addr,
|
||||||
|
value, false);
|
||||||
|
} else {
|
||||||
|
value = srng->u.dst_ring.tp;
|
||||||
|
|
||||||
|
if (value == srng->updated_tp ||
|
||||||
|
srng->force_cnt++ < HAL_DEL_WRITE_FORCE_UPDATE_THRES)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
hal_write_address_32_mb(hal_soc, srng->u.dst_ring.tp_addr,
|
||||||
|
value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
hal_srng_update_last_hptp(srng);
|
||||||
|
hal_srng_reg_his_add(srng, value);
|
||||||
|
qdf_atomic_inc(&hal_soc->stats.wstats.direct);
|
||||||
|
srng->wstats.direct++;
|
||||||
|
|
||||||
|
out_unlock:
|
||||||
|
SRNG_UNLOCK(&srng->lock);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline void hal_srng_update_last_hptp(struct hal_srng *srng)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif /* QCA_WIFI_QCA6750 */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hal_process_reg_write_q_elem() - process a register write queue element
|
* hal_process_reg_write_q_elem() - process a register write queue element
|
||||||
* @hal: hal_soc pointer
|
* @hal: hal_soc pointer
|
||||||
@@ -705,6 +771,8 @@ hal_process_reg_write_q_elem(struct hal_soc *hal,
|
|||||||
srng->u.dst_ring.tp, false);
|
srng->u.dst_ring.tp, false);
|
||||||
write_val = srng->u.dst_ring.tp;
|
write_val = srng->u.dst_ring.tp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hal_srng_update_last_hptp(srng);
|
||||||
hal_srng_reg_his_add(srng, write_val);
|
hal_srng_reg_his_add(srng, write_val);
|
||||||
|
|
||||||
q_elem->valid = 0;
|
q_elem->valid = 0;
|
||||||
@@ -1100,6 +1168,7 @@ void hal_delayed_reg_write(struct hal_soc *hal_soc,
|
|||||||
PLD_MHI_STATE_L0 ==
|
PLD_MHI_STATE_L0 ==
|
||||||
pld_get_mhi_state(hal_soc->qdf_dev->dev))) {
|
pld_get_mhi_state(hal_soc->qdf_dev->dev))) {
|
||||||
hal_write_address_32_mb(hal_soc, addr, value, false);
|
hal_write_address_32_mb(hal_soc, addr, value, false);
|
||||||
|
hal_srng_update_last_hptp(srng);
|
||||||
hal_srng_reg_his_add(srng, value);
|
hal_srng_reg_his_add(srng, value);
|
||||||
qdf_atomic_inc(&hal_soc->stats.wstats.direct);
|
qdf_atomic_inc(&hal_soc->stats.wstats.direct);
|
||||||
srng->wstats.direct++;
|
srng->wstats.direct++;
|
||||||
@@ -1136,6 +1205,7 @@ void hal_delayed_reg_write(struct hal_soc *hal_soc,
|
|||||||
qdf_atomic_inc(&hal_soc->stats.wstats.direct);
|
qdf_atomic_inc(&hal_soc->stats.wstats.direct);
|
||||||
srng->wstats.direct++;
|
srng->wstats.direct++;
|
||||||
hal_write_address_32_mb(hal_soc, addr, value, false);
|
hal_write_address_32_mb(hal_soc, addr, value, false);
|
||||||
|
hal_srng_update_last_hptp(srng);
|
||||||
hal_srng_reg_his_add(srng, value);
|
hal_srng_reg_his_add(srng, value);
|
||||||
} else {
|
} else {
|
||||||
hal_reg_write_enqueue(hal_soc, srng, addr, value);
|
hal_reg_write_enqueue(hal_soc, srng, addr, value);
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016-2021 The Linux Foundation. All rights reserved.
|
* Copyright (c) 2016-2021 The Linux Foundation. All rights reserved.
|
||||||
* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for
|
* Permission to use, copy, modify, and/or distribute this software for
|
||||||
* any purpose with or without fee is hereby granted, provided that the
|
* any purpose with or without fee is hereby granted, provided that the
|
||||||
@@ -344,6 +344,12 @@ ce_recv_buf_enqueue_srng(struct CE_handle *copyeng,
|
|||||||
return QDF_STATUS_E_IO;
|
return QDF_STATUS_E_IO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* HP/TP update if any should happen only once per interrupt,
|
||||||
|
* therefore checking for CE receive_count.
|
||||||
|
*/
|
||||||
|
hal_srng_check_and_update_hptp(scn->hal_soc, dest_ring->srng_ctx,
|
||||||
|
!CE_state->receive_count);
|
||||||
|
|
||||||
if (hal_srng_access_start(scn->hal_soc, dest_ring->srng_ctx)) {
|
if (hal_srng_access_start(scn->hal_soc, dest_ring->srng_ctx)) {
|
||||||
qdf_spin_unlock_bh(&CE_state->ce_index_lock);
|
qdf_spin_unlock_bh(&CE_state->ce_index_lock);
|
||||||
return QDF_STATUS_E_FAILURE;
|
return QDF_STATUS_E_FAILURE;
|
||||||
@@ -446,6 +452,12 @@ ce_completed_recv_next_nolock_srng(struct CE_state *CE_state,
|
|||||||
int nbytes;
|
int nbytes;
|
||||||
struct ce_srng_dest_status_desc dest_status_info;
|
struct ce_srng_dest_status_desc dest_status_info;
|
||||||
|
|
||||||
|
/* HP/TP update if any should happen only once per interrupt,
|
||||||
|
* therefore checking for CE receive_count.
|
||||||
|
*/
|
||||||
|
hal_srng_check_and_update_hptp(scn->hal_soc, status_ring->srng_ctx,
|
||||||
|
!CE_state->receive_count);
|
||||||
|
|
||||||
if (hal_srng_access_start(scn->hal_soc, status_ring->srng_ctx))
|
if (hal_srng_access_start(scn->hal_soc, status_ring->srng_ctx))
|
||||||
return QDF_STATUS_E_FAILURE;
|
return QDF_STATUS_E_FAILURE;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user