diff --git a/hal/wifi3.0/hal_api.h b/hal/wifi3.0/hal_api.h index f44d110076..e87aac6c0b 100644 --- a/hal/wifi3.0/hal_api.h +++ b/hal/wifi3.0/hal_api.h @@ -877,6 +877,27 @@ static inline int hal_get_reg_write_pending_work(void *hal_soc) } #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 * @soc: soc handle diff --git a/hal/wifi3.0/hal_internal.h b/hal/wifi3.0/hal_internal.h index 32b9e973f0..cd276afae5 100644 --- a/hal/wifi3.0/hal_internal.h +++ b/hal/wifi3.0/hal_internal.h @@ -868,6 +868,12 @@ struct hal_srng { /* srng specific delayed write stats */ struct hal_reg_write_srng_stats wstats; + + union { + uint32_t updated_hp; + uint32_t updated_tp; + }; + uint32_t force_cnt; #endif #ifdef WLAN_DP_SRNG_USAGE_WM_TRACKING struct hal_srng_high_wm_info high_wm; diff --git a/hal/wifi3.0/hal_srng.c b/hal/wifi3.0/hal_srng.c index c193170843..7b7194a2de 100644 --- a/hal/wifi3.0/hal_srng.c +++ b/hal/wifi3.0/hal_srng.c @@ -673,6 +673,72 @@ int hal_get_reg_write_pending_work(void *hal_soc) #define HAL_REG_WRITE_QUEUE_LEN 32 #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: hal_soc pointer @@ -705,6 +771,8 @@ hal_process_reg_write_q_elem(struct hal_soc *hal, srng->u.dst_ring.tp, false); write_val = srng->u.dst_ring.tp; } + + hal_srng_update_last_hptp(srng); hal_srng_reg_his_add(srng, write_val); q_elem->valid = 0; @@ -1100,6 +1168,7 @@ void hal_delayed_reg_write(struct hal_soc *hal_soc, PLD_MHI_STATE_L0 == pld_get_mhi_state(hal_soc->qdf_dev->dev))) { hal_write_address_32_mb(hal_soc, 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++; @@ -1136,6 +1205,7 @@ void hal_delayed_reg_write(struct hal_soc *hal_soc, qdf_atomic_inc(&hal_soc->stats.wstats.direct); srng->wstats.direct++; hal_write_address_32_mb(hal_soc, addr, value, false); + hal_srng_update_last_hptp(srng); hal_srng_reg_his_add(srng, value); } else { hal_reg_write_enqueue(hal_soc, srng, addr, value); diff --git a/hif/src/ce/ce_service_srng.c b/hif/src/ce/ce_service_srng.c index f05cc4be35..42ce1e1d30 100644 --- a/hif/src/ce/ce_service_srng.c +++ b/hif/src/ce/ce_service_srng.c @@ -1,6 +1,6 @@ /* * 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 * 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; } + /* 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)) { qdf_spin_unlock_bh(&CE_state->ce_index_lock); return QDF_STATUS_E_FAILURE; @@ -446,6 +452,12 @@ ce_completed_recv_next_nolock_srng(struct CE_state *CE_state, int nbytes; 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)) return QDF_STATUS_E_FAILURE;