瀏覽代碼

qcacmn: Interrupt handling changes for UMAC HW reset interrupt

Create a HIF context for UMAC reset handler, register the datapath UMAC HW
reset callback handler with HIF layer, request for UMAC HW reset interrupt,
and schedule a high priority tasklet to process the interrupt in which
call the registered DP callback handler.

CRs-Fixed: 3184312
Change-Id: Iefc811bf0d1b093c3c63bf2238c94a1448f4f139
Shiva Krishna Pittala 3 年之前
父節點
當前提交
053f59e4f0
共有 5 個文件被更改,包括 288 次插入5 次删除
  1. 86 0
      dp/wifi3.0/dp_umac_reset.c
  2. 26 4
      dp/wifi3.0/dp_umac_reset.h
  3. 39 0
      hif/inc/hif.h
  4. 116 1
      hif/src/hif_exec.c
  5. 21 0
      hif/src/hif_main.h

+ 86 - 0
dp/wifi3.0/dp_umac_reset.c

@@ -15,6 +15,7 @@
  */
 #include <dp_types.h>
 #include <wlan_cfg.h>
+#include <hif.h>
 
 /**
  * dp_get_umac_reset_intr_ctx() - Get the interrupt context to be used by
@@ -60,6 +61,7 @@ QDF_STATUS dp_soc_umac_reset_init(struct dp_soc *soc)
 	umac_reset_ctx = &soc->umac_reset_ctx;
 	qdf_mem_zero(umac_reset_ctx, sizeof(*umac_reset_ctx));
 
+	umac_reset_ctx->supported = true;
 	umac_reset_ctx->current_state = UMAC_RESET_STATE_WAIT_FOR_PRE_RESET;
 
 	status = dp_get_umac_reset_intr_ctx(soc, &umac_reset_ctx->intr_offset);
@@ -89,3 +91,87 @@ QDF_STATUS dp_soc_umac_reset_init(struct dp_soc *soc)
 	return QDF_STATUS_SUCCESS;
 }
 
+/**
+ * dp_umac_reset_rx_event_handler() - Main Rx event handler for UMAC reset
+ * @dp_ctx: Interrupt context corresponding to UMAC reset
+ *
+ * Return: 0 incase of success, else failure
+ */
+static int dp_umac_reset_rx_event_handler(void *dp_ctx)
+{
+	/* Note: This will be implemented in an upcoming change */
+	return 0;
+}
+
+QDF_STATUS dp_umac_reset_interrupt_attach(struct dp_soc *soc)
+{
+	struct dp_soc_umac_reset_ctx *umac_reset_ctx;
+	int msi_vector_count, ret;
+	uint32_t msi_base_data, msi_vector_start;
+	uint32_t umac_reset_vector, umac_reset_irq;
+
+	if (!soc) {
+		dp_umac_reset_err("DP SOC is null");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	umac_reset_ctx = &soc->umac_reset_ctx;
+
+	/* return if feature is not supported */
+	if (!umac_reset_ctx->supported) {
+		dp_umac_reset_info("UMAC reset is not supported on this SOC");
+		return QDF_STATUS_SUCCESS;
+	}
+
+	if (pld_get_enable_intx(soc->osdev->dev)) {
+		dp_umac_reset_err("UMAC reset is not supported in legacy interrupt mode");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	ret = pld_get_user_msi_assignment(soc->osdev->dev, "DP",
+					  &msi_vector_count, &msi_base_data,
+					  &msi_vector_start);
+	if (ret) {
+		dp_umac_reset_err("UMAC reset is only supported in MSI interrupt mode");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (umac_reset_ctx->intr_offset < 0 ||
+	    umac_reset_ctx->intr_offset >= WLAN_CFG_INT_NUM_CONTEXTS) {
+		dp_umac_reset_err("Invalid interrupt offset");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	umac_reset_vector = msi_vector_start +
+			       (umac_reset_ctx->intr_offset % msi_vector_count);
+
+	/* Get IRQ number */
+	umac_reset_irq = pld_get_msi_irq(soc->osdev->dev, umac_reset_vector);
+
+	/* Finally register to this IRQ from HIF layer */
+	return hif_register_umac_reset_handler(
+				soc->hif_handle,
+				dp_umac_reset_rx_event_handler,
+				&soc->intr_ctx[umac_reset_ctx->intr_offset],
+				umac_reset_irq);
+}
+
+QDF_STATUS dp_umac_reset_interrupt_detach(struct dp_soc *soc)
+{
+	struct dp_soc_umac_reset_ctx *umac_reset_ctx;
+
+	if (!soc) {
+		dp_umac_reset_err("DP SOC is null");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	umac_reset_ctx = &soc->umac_reset_ctx;
+
+	/* return if feature is not supported */
+	if (!umac_reset_ctx->supported) {
+		dp_umac_reset_info("UMAC reset is not supported on this SOC");
+		return QDF_STATUS_SUCCESS;
+	}
+
+	return hif_unregister_umac_reset_handler(soc->hif_handle);
+}

+ 26 - 4
dp/wifi3.0/dp_umac_reset.h

@@ -17,10 +17,10 @@
 #ifndef _DP_UMAC_RESET_H_
 #define _DP_UMAC_RESET_H_
 
-#ifdef DP_UMAC_HW_RESET_SUPPORT
-
 #include <qdf_types.h>
+struct dp_soc;
 
+#ifdef DP_UMAC_HW_RESET_SUPPORT
 #define dp_umac_reset_alert(params...) \
 	QDF_TRACE_FATAL(QDF_MODULE_ID_DP_UMAC_RESET, params)
 #define dp_umac_reset_err(params...) \
@@ -35,8 +35,6 @@
 	QDF_TRACE_DEBUG(QDF_MODULE_ID_DP_UMAC_RESET, params)
 
 #define DP_UMAC_RESET_SHMEM_ALIGN 8
-
-struct dp_soc;
 /**
  * enum umac_reset_state - States required for UMAC reset state machine
  * @UMAC_RESET_STATE_WAIT_FOR_PRE_RESET: Waiting for the PRE_RESET event
@@ -75,6 +73,7 @@ struct umac_reset_shmem {
  * @shmem_vaddr_aligned: Virtual address of the shared memory (aligned)
  * @intr_offset: Offset of the UMAC reset interrupt w.r.t DP base interrupt
  * @current_state: current state of the UMAC reset state machine
+ * @supported: Whether UMAC reset is supported on this soc
  */
 struct dp_soc_umac_reset_ctx {
 	qdf_dma_addr_t shmem_paddr_unaligned;
@@ -83,6 +82,7 @@ struct dp_soc_umac_reset_ctx {
 	struct umac_reset_shmem *shmem_vaddr_aligned;
 	int intr_offset;
 	enum umac_reset_state current_state;
+	bool supported;
 };
 
 /**
@@ -92,5 +92,27 @@ struct dp_soc_umac_reset_ctx {
  * Return: QDF status of operation
  */
 QDF_STATUS dp_soc_umac_reset_init(struct dp_soc *soc);
+
+/**
+ * dp_umac_reset_interrupt_attach() - Register handlers for UMAC reset interrupt
+ * @soc: DP soc object
+ *
+ * Return: QDF status of operation
+ */
+QDF_STATUS dp_umac_reset_interrupt_attach(struct dp_soc *soc);
+
+/**
+ * dp_umac_reset_interrupt_detach() - Unregister UMAC reset interrupt handlers
+ * @soc: DP soc object
+ *
+ * Return: QDF status of operation
+ */
+QDF_STATUS dp_umac_reset_interrupt_detach(struct dp_soc *soc);
+#else
+static inline
+QDF_STATUS dp_soc_umac_reset_init(struct dp_soc *soc)
+{
+	return QDF_STATUS_SUCCESS;
+}
 #endif /* DP_UMAC_HW_RESET_SUPPORT */
 #endif /* _DP_UMAC_RESET_H_ */

+ 39 - 0
hif/inc/hif.h

@@ -2349,4 +2349,43 @@ void hif_set_grp_intr_affinity(struct hif_opaque_softc *scn,
  *  uint8_t: count for WMI eps in target svc map
  */
 uint8_t hif_get_max_wmi_ep(struct hif_opaque_softc *scn);
+
+#ifdef DP_UMAC_HW_RESET_SUPPORT
+/**
+ * hif_register_umac_reset_handler() - Register UMAC HW reset handler
+ * @hif_scn: hif opaque handle
+ * @handler: callback handler function
+ * @cb_ctx: context to passed to @handler
+ * @irq: irq number to be used for UMAC HW reset interrupt
+ *
+ * Return: QDF_STATUS of operation
+ */
+QDF_STATUS hif_register_umac_reset_handler(struct hif_opaque_softc *hif_scn,
+					   int (*handler)(void *cb_ctx),
+					   void *cb_ctx, int irq);
+
+/**
+ * hif_unregister_umac_reset_handler() - Unregister UMAC HW reset handler
+ * @hif_scn: hif opaque handle
+ *
+ * Return: QDF_STATUS of operation
+ */
+QDF_STATUS hif_unregister_umac_reset_handler(struct hif_opaque_softc *hif_scn);
+#else
+static inline
+QDF_STATUS hif_register_umac_reset_handler(struct hif_opaque_softc *hif_scn,
+					   int (*handler)(void *cb_ctx),
+					   void *cb_ctx, int irq)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static inline
+QDF_STATUS hif_unregister_umac_reset_handler(struct hif_opaque_softc *hif_scn)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+#endif /* DP_UMAC_HW_RESET_SUPPORT */
+
 #endif /* _HIF_H_ */

+ 116 - 1
hif/src/hif_exec.c

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2022 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
@@ -21,6 +21,8 @@
 #include <ce_main.h>
 #include "qdf_module.h"
 #include "qdf_net_if.h"
+#include <pld_common.h>
+
 /* mapping NAPI budget 0 to internal budget 0
  * NAPI budget 1 to internal budget [1,scaler -1]
  * NAPI budget 2 to internal budget [scaler, 2 * scaler - 1], etc
@@ -1129,3 +1131,116 @@ void hif_deregister_exec_group(struct hif_opaque_softc *hif_ctx,
 	}
 }
 qdf_export_symbol(hif_deregister_exec_group);
+
+#ifdef DP_UMAC_HW_RESET_SUPPORT
+/**
+ * hif_umac_reset_handler_tasklet() - Tasklet for UMAC HW reset interrupt
+ * @data: UMAC HW reset HIF context
+ *
+ * return: void
+ */
+static void hif_umac_reset_handler_tasklet(unsigned long data)
+{
+	struct hif_umac_reset_ctx *umac_reset_ctx =
+		(struct hif_umac_reset_ctx *)data;
+
+	/* call the callback handler */
+	umac_reset_ctx->cb_handler(umac_reset_ctx->cb_ctx);
+}
+
+/**
+ * hif_umac_reset_irq_handler() - Interrupt service routine of UMAC HW reset
+ * @irq: irq coming from kernel
+ * @ctx: UMAC HW reset HIF context
+ *
+ * return: IRQ_HANDLED if success, else IRQ_NONE
+ */
+static irqreturn_t hif_umac_reset_irq_handler(int irq, void *ctx)
+{
+	struct hif_umac_reset_ctx *umac_reset_ctx = ctx;
+
+	/* Schedule the tasklet and exit */
+	tasklet_hi_schedule(&umac_reset_ctx->intr_tq);
+
+	return IRQ_HANDLED;
+}
+
+QDF_STATUS hif_register_umac_reset_handler(struct hif_opaque_softc *hif_scn,
+					   int (*handler)(void *cb_ctx),
+					   void *cb_ctx, int irq)
+{
+	struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_scn);
+	struct hif_umac_reset_ctx *umac_reset_ctx;
+	int ret;
+
+	if (!hif_sc) {
+		hif_err("scn is null");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	umac_reset_ctx = &hif_sc->umac_reset_ctx;
+
+	umac_reset_ctx->cb_handler = handler;
+	umac_reset_ctx->cb_ctx = cb_ctx;
+	umac_reset_ctx->os_irq = irq;
+
+	/* Init the tasklet */
+	tasklet_init(&umac_reset_ctx->intr_tq,
+		     hif_umac_reset_handler_tasklet,
+		     (unsigned long)umac_reset_ctx);
+
+	/* Register the interrupt handler */
+	ret  = pfrm_request_irq(hif_sc->qdf_dev->dev, irq,
+				hif_umac_reset_irq_handler,
+				IRQF_SHARED | IRQF_NO_SUSPEND,
+				"umac_hw_reset_irq",
+				umac_reset_ctx);
+	if (ret) {
+		hif_err("request_irq failed: %d", ret);
+		return qdf_status_from_os_return(ret);
+	}
+
+	umac_reset_ctx->irq_configured = true;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+qdf_export_symbol(hif_register_umac_reset_handler);
+
+QDF_STATUS hif_unregister_umac_reset_handler(struct hif_opaque_softc *hif_scn)
+{
+	struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_scn);
+	struct hif_umac_reset_ctx *umac_reset_ctx;
+	int ret;
+
+	if (!hif_sc) {
+		hif_err("scn is null");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	umac_reset_ctx = &hif_sc->umac_reset_ctx;
+	if (!umac_reset_ctx->irq_configured) {
+		hif_err("unregister called without a prior IRQ configuration");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	ret  = pfrm_free_irq(hif_sc->qdf_dev->dev,
+			     umac_reset_ctx->os_irq,
+			     umac_reset_ctx);
+	if (ret) {
+		hif_err("free_irq failed: %d", ret);
+		return qdf_status_from_os_return(ret);
+	}
+	umac_reset_ctx->irq_configured = false;
+
+	tasklet_disable(&umac_reset_ctx->intr_tq);
+	tasklet_kill(&umac_reset_ctx->intr_tq);
+
+	umac_reset_ctx->cb_handler = NULL;
+	umac_reset_ctx->cb_ctx = NULL;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+qdf_export_symbol(hif_unregister_umac_reset_handler);
+#endif

+ 21 - 0
hif/src/hif_main.h

@@ -204,6 +204,24 @@ struct hif_cfg {
 	uint8_t ce_status_ring_batch_count_threshold;
 };
 
+#ifdef DP_UMAC_HW_RESET_SUPPORT
+/**
+ * struct hif_umac_reset_ctx - UMAC HW reset context at HIF layer
+ * @intr_tq: Tasklet structure
+ * @cb_handler: Callback handler
+ * @cb_ctx: Argument to be passed to @cb_handler
+ * @os_irq: Interrupt number for this IRQ
+ * @irq_configured: Whether the IRQ has been configured
+ */
+struct hif_umac_reset_ctx {
+	struct tasklet_struct intr_tq;
+	int (*cb_handler)(void *cb_ctx);
+	void *cb_ctx;
+	uint32_t os_irq;
+	bool irq_configured;
+};
+#endif
+
 struct hif_softc {
 	struct hif_opaque_softc osc;
 	struct hif_config_info hif_config;
@@ -323,6 +341,9 @@ struct hif_softc {
 	uint64_t cmem_start;
 	/* CMEM size target reserved */
 	uint64_t cmem_size;
+#ifdef DP_UMAC_HW_RESET_SUPPORT
+	struct hif_umac_reset_ctx umac_reset_ctx;
+#endif
 };
 
 static inline