Quellcode durchsuchen

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
Manikanta Pubbisetty vor 1 Jahr
Ursprung
Commit
e2e92aa7d6
4 geänderte Dateien mit 110 neuen und 1 gelöschten Zeilen
  1. 21 0
      hal/wifi3.0/hal_api.h
  2. 6 0
      hal/wifi3.0/hal_internal.h
  3. 70 0
      hal/wifi3.0/hal_srng.c
  4. 13 1
      hif/src/ce/ce_service_srng.c

+ 21 - 0
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

+ 6 - 0
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;

+ 70 - 0
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);

+ 13 - 1
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;