qcacmn: Add framework for external group interrupt handling

Change-Id: I68a3c597e452e1975a97f9262870e16538f6dc4c
CRs-Fixed: 1042915
diff --git a/hif/inc/hif.h b/hif/inc/hif.h
index 8468117..c648caf 100644
--- a/hif/inc/hif.h
+++ b/hif/inc/hif.h
@@ -100,8 +100,52 @@
 #define TARGET_TYPE_QCA8074   20
 #endif
 
+/* enum hif_ic_irq - enum defining integrated chip irq numbers
+ * defining irq nubers that can be used by external modules like datapath
+ */
+enum hif_ic_irq {
+	host2wbm_desc_feed = 18,
+	host2reo_re_injection,
+	host2reo_command,
+	host2rxdma_monitor_ring3,
+	host2rxdma_monitor_ring2,
+	host2rxdma_monitor_ring1,
+	reo2ost_exception,
+	wbm2host_rx_release,
+	reo2host_status,
+	reo2host_destination_ring4,
+	reo2host_destination_ring3,
+	reo2host_destination_ring2,
+	reo2host_destination_ring1,
+	rxdma2host_monitor_destination_mac3,
+	rxdma2host_monitor_destination_mac2,
+	rxdma2host_monitor_destination_mac1,
+	ppdu_end_interrupts_mac3,
+	ppdu_end_interrupts_mac2,
+	ppdu_end_interrupts_mac1,
+	rxdma2host_monitor_status_ring_mac3,
+	rxdma2host_monitor_status_ring_mac2,
+	rxdma2host_monitor_status_ring_mac1,
+	host2rxdma_host_buf_ring_mac3,
+	host2rxdma_host_buf_ring_mac2,
+	host2rxdma_host_buf_ring_mac1,
+	rxdma2host_destination_ring_mac3,
+	rxdma2host_destination_ring_mac2,
+	rxdma2host_destination_ring_mac1,
+	host2tcl_input_ring4,
+	host2tcl_input_ring3,
+	host2tcl_input_ring2,
+	host2tcl_input_ring1,
+	wbm2host_tx_completions_ring3,
+	wbm2host_tx_completions_ring2,
+	wbm2host_tx_completions_ring1,
+	tcl2host_status_ring,
+};
+
 struct CE_state;
 #define CE_COUNT_MAX 12
+#define HIF_MAX_GRP_IRQ 16
+#define HIF_MAX_GROUP 8
 
 #ifdef CONFIG_SLUB_DEBUG_ON
 #define QCA_NAPI_BUDGET    64
@@ -407,6 +451,7 @@
 		       uint8_t *data, int nbytes);
 
 typedef void (*fastpath_msg_handler)(void *, qdf_nbuf_t *, uint32_t);
+typedef uint32_t (*ext_intr_handler)(void *, uint32_t);
 
 /*
  * Set the FASTPATH_mode_on flag in sc, for use by data path
@@ -682,6 +727,10 @@
 void hif_fake_apps_suspend(hdd_fake_resume_callback callback);
 #endif
 
+uint32_t hif_register_ext_group_int_handler(struct hif_opaque_softc *hif_ctx,
+		uint32_t numirq, uint32_t irq[], ext_intr_handler handler,
+		void *context);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/hif/inc/hif_napi.h b/hif/inc/hif_napi.h
index 243301f..edf1fd0 100644
--- a/hif/inc/hif_napi.h
+++ b/hif/inc/hif_napi.h
@@ -194,6 +194,9 @@
 static inline int hif_napi_enabled(struct hif_opaque_softc *hif, int ce)
 { return 0; }
 
+static inline int hif_ext_napi_enabled(struct hif_opaque_softc *hif, int ce)
+{ return 0; }
+
 /* called from hdd (napi_poll), using napi id as a selector */
 static inline void hif_napi_enable_irq(struct hif_opaque_softc *hif, int id)
 { return; }
@@ -201,6 +204,10 @@
 static inline int hif_napi_schedule(struct hif_opaque_softc *hif, int ce_id)
 { return 0; }
 
+static inline int hif_napi_schedule_grp(struct hif_opaque_softc *hif,
+		uint32_t grp_id)
+{ return 0; }
+
 static inline int hif_napi_poll(struct napi_struct *napi, int budget)
 { return -EPERM; }
 
diff --git a/hif/src/ce/ce_main.h b/hif/src/ce/ce_main.h
index e58f30a..fbc0f6c 100644
--- a/hif/src/ce/ce_main.h
+++ b/hif/src/ce/ce_main.h
@@ -113,6 +113,18 @@
 	void *hif_ce_state;
 };
 
+struct hif_ext_group_entry {
+	uint32_t numirq;
+	uint32_t irq[HIF_MAX_GRP_IRQ];
+	uint32_t grp_id;
+	void *context;
+	ext_intr_handler handler;
+	struct tasklet_struct intr_tq;
+	bool configured;
+	bool inited;
+	void *hif_state;
+};
+
 struct ce_intr_stats {
 	uint32_t ce_per_cpu[CE_COUNT_MAX][QDF_MAX_AVAILABLE_CPU];
 };
@@ -121,6 +133,8 @@
 	struct hif_softc ol_sc;
 	bool started;
 	struct ce_tasklet_entry tasklets[CE_COUNT_MAX];
+	struct hif_ext_group_entry hif_ext_group[HIF_MAX_GROUP];
+	uint32_t hif_num_extgroup;
 	qdf_spinlock_t keep_awake_lock;
 	qdf_spinlock_t irq_reg_lock;
 	unsigned int keep_awake_count;
diff --git a/hif/src/dispatcher/ahb_api.h b/hif/src/dispatcher/ahb_api.h
index 3a715fd..75f1506 100644
--- a/hif/src/dispatcher/ahb_api.h
+++ b/hif/src/dispatcher/ahb_api.h
@@ -35,6 +35,8 @@
 int hif_ahb_bus_configure(struct hif_softc *scn);
 void hif_ahb_irq_disable(struct hif_softc *scn, int ce_id);
 void hif_ahb_irq_enable(struct hif_softc *scn, int ce_id);
+void hif_ahb_grp_irq_disable(struct hif_softc *scn, uint32_t grp_id);
+void hif_ahb_grp_irq_enable(struct hif_softc *scn, uint32_t grp_id);
 int hif_ahb_dump_registers(struct hif_softc *scn);
 
 int hif_ahb_configure_legacy_irq(struct hif_pci_softc *sc);
diff --git a/hif/src/dispatcher/dummy.c b/hif/src/dispatcher/dummy.c
index 1690e05..6c2ebd7 100644
--- a/hif/src/dispatcher/dummy.c
+++ b/hif/src/dispatcher/dummy.c
@@ -233,6 +233,16 @@
 {}
 
 /**
+ * hif_dummy_grp_irq_enable - dummy call
+ * hif_ctx: hif context
+ * @irq_id: grp id
+ *
+ * Return: none
+ */
+void hif_dummy_grp_irq_enable(struct hif_softc *hif_sc, uint32_t grp_id)
+{}
+
+/**
  * hif_dummy_irq_disable - dummy call
  * hif_ctx: hif context
  * @irq_id: irq id
@@ -243,6 +253,15 @@
 {}
 
 /**
+ * hif_dummy_grp_irq_disable- dummy call
+ * hif_ctx: hif context
+ * @grp_id: grp id
+ *
+ * Return: none
+ */
+void hif_dummy_grp_irq_disable(struct hif_softc *hif_sc, uint32_t grp_id)
+{}
+/**
  * hif_dummy_dump_registers - dummy call
  * hif_sc: hif context
  *
diff --git a/hif/src/dispatcher/dummy.h b/hif/src/dispatcher/dummy.h
index b531c1d..6d8a2c7 100644
--- a/hif/src/dispatcher/dummy.h
+++ b/hif/src/dispatcher/dummy.h
@@ -47,6 +47,8 @@
 void hif_dummy_cancel_deferred_target_sleep(struct hif_softc *hif_sc);
 void hif_dummy_irq_enable(struct hif_softc *hif_sc, int irq_id);
 void hif_dummy_irq_disable(struct hif_softc *hif_sc, int irq_id);
+void hif_dummy_grp_irq_enable(struct hif_softc *hif_sc, uint32_t grp_id);
+void hif_dummy_grp_irq_disable(struct hif_softc *hif_sc, uint32_t grp_id);
 int hif_dummy_dump_registers(struct hif_softc *hif_sc);
 void hif_dummy_dump_target_memory(struct hif_softc *hif_sc, void *ramdump_base,
 				  uint32_t address, uint32_t size);
diff --git a/hif/src/dispatcher/multibus.c b/hif/src/dispatcher/multibus.c
index 5643b6f..8b4e463 100644
--- a/hif/src/dispatcher/multibus.c
+++ b/hif/src/dispatcher/multibus.c
@@ -59,6 +59,8 @@
 	bus_ops->hif_bus_reset_resume = &hif_dummy_bus_reset_resume;
 	bus_ops->hif_bus_suspend_noirq = &hif_dummy_bus_suspend_noirq;
 	bus_ops->hif_bus_resume_noirq = &hif_dummy_bus_resume_noirq;
+	bus_ops->hif_grp_irq_disable = &hif_dummy_grp_irq_disable;
+	bus_ops->hif_grp_irq_enable = &hif_dummy_grp_irq_enable;
 }
 
 #define NUM_OPS (sizeof(struct hif_bus_ops) / sizeof(void *))
@@ -286,11 +288,21 @@
 	hif_sc->bus_ops.hif_irq_enable(hif_sc, irq_id);
 }
 
+void hif_grp_irq_enable(struct hif_softc *hif_sc, uint32_t grp_id)
+{
+	hif_sc->bus_ops.hif_grp_irq_enable(hif_sc, grp_id);
+}
+
 void hif_irq_disable(struct hif_softc *hif_sc, int irq_id)
 {
 	hif_sc->bus_ops.hif_irq_disable(hif_sc, irq_id);
 }
 
+void hif_grp_irq_disable(struct hif_softc *hif_sc, uint32_t grp_id)
+{
+	hif_sc->bus_ops.hif_grp_irq_disable(hif_sc, grp_id);
+}
+
 int hif_dump_registers(struct hif_opaque_softc *hif_hdl)
 {
 	struct hif_softc *hif_sc = HIF_GET_SOFTC(hif_hdl);
diff --git a/hif/src/dispatcher/multibus.h b/hif/src/dispatcher/multibus.h
index 6342363..e90a945 100644
--- a/hif/src/dispatcher/multibus.h
+++ b/hif/src/dispatcher/multibus.h
@@ -61,7 +61,9 @@
 	void (*hif_stop)(struct hif_softc *hif_sc);
 	void (*hif_cancel_deferred_target_sleep)(struct hif_softc *hif_sc);
 	void (*hif_irq_disable)(struct hif_softc *hif_sc, int ce_id);
+	void (*hif_grp_irq_disable)(struct hif_softc *hif_sc, uint32_t grp_id);
 	void (*hif_irq_enable)(struct hif_softc *hif_sc, int ce_id);
+	void (*hif_grp_irq_enable)(struct hif_softc *hif_sc, uint32_t grp_id);
 	int (*hif_dump_registers)(struct hif_softc *hif_sc);
 	void (*hif_dump_target_memory)(struct hif_softc *hif_sc,
 				       void *ramdump_base,
diff --git a/hif/src/dispatcher/multibus_ahb.c b/hif/src/dispatcher/multibus_ahb.c
index d1bee78..d67c9ec 100644
--- a/hif/src/dispatcher/multibus_ahb.c
+++ b/hif/src/dispatcher/multibus_ahb.c
@@ -66,6 +66,8 @@
 		&hif_dummy_enable_power_management;
 	bus_ops->hif_disable_power_management =
 		&hif_dummy_disable_power_management;
+	bus_ops->hif_grp_irq_disable = &hif_ahb_grp_irq_disable;
+	bus_ops->hif_grp_irq_enable = &hif_ahb_grp_irq_enable;
 
 	return QDF_STATUS_SUCCESS;
 }
diff --git a/hif/src/dispatcher/multibus_pci.c b/hif/src/dispatcher/multibus_pci.c
index 5459105..44f2953 100644
--- a/hif/src/dispatcher/multibus_pci.c
+++ b/hif/src/dispatcher/multibus_pci.c
@@ -85,6 +85,8 @@
 		&hif_pci_display_stats;
 	bus_ops->hif_clear_stats =
 		&hif_pci_clear_stats;
+	bus_ops->hif_grp_irq_disable = &hif_dummy_grp_irq_disable;
+	bus_ops->hif_grp_irq_enable = &hif_dummy_grp_irq_enable;
 
 	return QDF_STATUS_SUCCESS;
 }
diff --git a/hif/src/dispatcher/multibus_sdio.c b/hif/src/dispatcher/multibus_sdio.c
index fc27202..1f957c4 100644
--- a/hif/src/dispatcher/multibus_sdio.c
+++ b/hif/src/dispatcher/multibus_sdio.c
@@ -64,6 +64,8 @@
 		&hif_dummy_enable_power_management;
 	bus_ops->hif_disable_power_management =
 		&hif_dummy_disable_power_management;
+	bus_ops->hif_grp_irq_disable = &hif_dummy_grp_irq_disable;
+	bus_ops->hif_grp_irq_enable = &hif_dummy_grp_irq_enable;
 
 	return QDF_STATUS_SUCCESS;
 }
diff --git a/hif/src/dispatcher/multibus_snoc.c b/hif/src/dispatcher/multibus_snoc.c
index 2dc80d6..3d31077 100644
--- a/hif/src/dispatcher/multibus_snoc.c
+++ b/hif/src/dispatcher/multibus_snoc.c
@@ -79,6 +79,8 @@
 		&hif_snoc_display_stats;
 	bus_ops->hif_clear_stats =
 		&hif_snoc_clear_stats;
+	bus_ops->hif_grp_irq_disable = &hif_dummy_grp_irq_disable;
+	bus_ops->hif_grp_irq_enable = &hif_dummy_grp_irq_enable;
 
 	return QDF_STATUS_SUCCESS;
 }
diff --git a/hif/src/hif_io32.h b/hif/src/hif_io32.h
index 8c39893..8c1453f 100644
--- a/hif/src/hif_io32.h
+++ b/hif/src/hif_io32.h
@@ -104,6 +104,7 @@
 
 void hif_irq_enable(struct hif_softc *scn, int irq_id);
 void hif_irq_disable(struct hif_softc *scn, int irq_id);
-
+void hif_grp_irq_enable(struct hif_softc *scn, uint32_t grp_id);
+void hif_grp_irq_disable(struct hif_softc *scn, uint32_t grp_id);
 
 #endif /* __HIF_IO32_H__ */
diff --git a/hif/src/hif_main.c b/hif/src/hif_main.c
index 96c3241..936a451 100644
--- a/hif/src/hif_main.c
+++ b/hif/src/hif_main.c
@@ -375,6 +375,7 @@
 	scn->qdf_dev = qdf_ctx;
 	scn->hif_con_param = mode;
 	qdf_atomic_init(&scn->active_tasklet_cnt);
+	qdf_atomic_init(&scn->active_grp_tasklet_cnt);
 	qdf_atomic_init(&scn->link_suspended);
 	qdf_atomic_init(&scn->tasklet_from_intr);
 	qdf_mem_copy(&scn->callbacks, cbk, sizeof(struct hif_driver_state_callbacks));
@@ -1017,3 +1018,93 @@
 		hif_usb_ramdump_handler();
 }
 #endif
+
+/**
+ * hif_register_ext_group_int_handler() - API to register external group
+ * interrupt handler.
+ * @hif_ctx : HIF Context
+ * @numirq: number of irq's in the group
+ * @irq: array of irq values
+ * @ext_intr_handler: callback interrupt handler function
+ * @context: context to passed in callback
+ *
+ * Return: status
+ */
+uint32_t hif_register_ext_group_int_handler(struct hif_opaque_softc *hif_ctx,
+		uint32_t numirq, uint32_t irq[], ext_intr_handler handler,
+		void *context)
+{
+	struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx);
+	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn);
+	struct hif_ext_group_entry *hif_ext_group;
+
+	if (scn->hif_init_done) {
+		HIF_ERROR("%s Called after HIF initialization \n", __func__);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (hif_state->hif_num_extgroup >= HIF_MAX_GROUP) {
+		HIF_ERROR("%s Max groups reached\n", __func__);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (numirq >= HIF_MAX_GRP_IRQ) {
+		HIF_ERROR("%s invalid numirq\n", __func__);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	hif_ext_group = &hif_state->hif_ext_group[hif_state->hif_num_extgroup];
+
+	hif_ext_group->numirq = numirq;
+	qdf_mem_copy(&hif_ext_group->irq[0], irq, numirq * sizeof(irq[0]));
+	hif_ext_group->context = context;
+	hif_ext_group->handler = handler;
+	hif_ext_group->configured = true;
+	hif_ext_group->grp_id = hif_state->hif_num_extgroup;
+
+	hif_state->hif_num_extgroup++;
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * hif_ext_grp_tasklet() - grp tasklet
+ * data: context
+ *
+ * return: void
+ */
+void hif_ext_grp_tasklet(unsigned long data)
+{
+	struct hif_ext_group_entry *hif_ext_group =
+			(struct hif_ext_group_entry *)data;
+	struct HIF_CE_state *hif_state = hif_ext_group->hif_state;
+	struct hif_softc *scn = HIF_GET_SOFTC(hif_state);
+
+	if (hif_ext_group->grp_id < HIF_MAX_GROUP) {
+		hif_ext_group->handler(hif_ext_group->context, HIF_MAX_BUDGET);
+		hif_grp_irq_enable(scn, hif_ext_group->grp_id);
+	} else {
+		HIF_ERROR("%s: ERROR - invalid grp_id = %d",
+		       __func__, hif_ext_group->grp_id);
+	}
+
+	qdf_atomic_dec(&scn->active_grp_tasklet_cnt);
+}
+
+/**
+ * hif_grp_tasklet_kill() - grp tasklet kill
+ * scn: hif_softc
+ *
+ * return: void
+ */
+void hif_grp_tasklet_kill(struct hif_softc *scn)
+{
+	int i;
+	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn);
+
+	for (i = 0; i < HIF_MAX_GROUP; i++)
+		if (hif_state->hif_ext_group[i].inited) {
+			tasklet_kill(&hif_state->hif_ext_group[i].intr_tq);
+			hif_state->hif_ext_group[i].inited = false;
+		}
+	qdf_atomic_set(&scn->active_grp_tasklet_cnt, 0);
+}
diff --git a/hif/src/hif_main.h b/hif/src/hif_main.h
index 7e5d62c..0d0ae16 100644
--- a/hif/src/hif_main.h
+++ b/hif/src/hif_main.h
@@ -51,6 +51,8 @@
 #define HIF_MIN_SLEEP_INACTIVITY_TIME_MS     50
 #define HIF_SLEEP_INACTIVITY_TIMER_PERIOD_MS 60
 
+#define HIF_MAX_BUDGET 0xFFFF
+
 /*
  * This macro implementation is exposed for efficiency only.
  * The implementation may change and callers should
@@ -136,6 +138,7 @@
 	/* No of copy engines supported */
 	unsigned int ce_count;
 	atomic_t active_tasklet_cnt;
+	atomic_t active_grp_tasklet_cnt;
 	atomic_t link_suspended;
 	uint32_t *vaddr_rri_on_ddr;
 	int linkstate_vote;
@@ -218,4 +221,6 @@
 static inline void hif_usb_get_hw_info(struct hif_softc *scn) {}
 static inline void hif_ramdump_handler(struct hif_opaque_softc *scn) {}
 #endif
+void hif_ext_grp_tasklet(unsigned long data);
+void hif_grp_tasklet_kill(struct hif_softc *scn);
 #endif /* __HIF_MAIN_H__ */
diff --git a/hif/src/pcie/if_pci.c b/hif/src/pcie/if_pci.c
index 0558d3d..2a280fb 100644
--- a/hif/src/pcie/if_pci.c
+++ b/hif/src/pcie/if_pci.c
@@ -2952,8 +2952,10 @@
 	hif_free_msi_ctx(scn);
 	/* Cancel the pending tasklet */
 	ce_tasklet_kill(scn);
+	hif_grp_tasklet_kill(scn);
 	tasklet_kill(&sc->intr_tq);
 	qdf_atomic_set(&scn->active_tasklet_cnt, 0);
+	qdf_atomic_set(&scn->active_grp_tasklet_cnt, 0);
 }
 
 /* Function to reset SoC */
diff --git a/hif/src/snoc/if_ahb.c b/hif/src/snoc/if_ahb.c
index 23c2b6a..0b9bf31 100644
--- a/hif/src/snoc/if_ahb.c
+++ b/hif/src/snoc/if_ahb.c
@@ -32,6 +32,7 @@
 #include "if_pci.h"
 #include "ahb_api.h"
 #include "pci_api.h"
+#include "hif_napi.h"
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
 #define IRQF_DISABLED 0x00000020
@@ -40,6 +41,7 @@
 #define HIF_IC_CE0_IRQ_OFFSET 4
 #define HIF_IC_MAX_IRQ 54
 
+static uint8_t ic_irqnum[HIF_IC_MAX_IRQ];
 /* integrated chip irq names */
 const char *ic_irqname[HIF_IC_MAX_IRQ] = {
 "misc_pulse1",
@@ -98,7 +100,6 @@
 "tcl2host_status_ring",
 };
 
-irqreturn_t hif_ahb_interrupt_handler(int irq, void *context);
 /**
  * hif_disable_isr() - disable isr
  *
@@ -114,8 +115,10 @@
 
 	hif_nointrs(scn);
 	ce_tasklet_kill(scn);
+	hif_grp_tasklet_kill(scn);
 	tasklet_kill(&sc->intr_tq);
 	qdf_atomic_set(&scn->active_tasklet_cnt, 0);
+	qdf_atomic_set(&scn->active_grp_tasklet_cnt, 0);
 }
 
 /**
@@ -246,17 +249,19 @@
 	int ret = 0;
 	struct hif_softc *scn = HIF_GET_SOFTC(sc);
 	struct platform_device *pdev = (struct platform_device *)sc->pdev;
-	struct HIF_CE_state *hif_ce_state = HIF_GET_CE_STATE(scn);
+	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn);
+	struct hif_ext_group_entry *hif_ext_group;
 	int irq = 0;
-	int i;
+	int i, j;
 
 	/* configure per CE interrupts */
 	for (i = 0; i < scn->ce_count; i++) {
 		irq = platform_get_irq_byname(pdev, ic_irqname[HIF_IC_CE0_IRQ_OFFSET + i]);
+		ic_irqnum[HIF_IC_CE0_IRQ_OFFSET + i] = irq;
 		ret = request_irq(irq ,
 				hif_ahb_interrupt_handler,
 				IRQF_TRIGGER_RISING, ic_irqname[HIF_IC_CE0_IRQ_OFFSET + i],
-				&hif_ce_state->tasklets[i]);
+				&hif_state->tasklets[i]);
 		if (ret) {
 			dev_err(&pdev->dev, "ath_request_irq failed\n");
 			ret = -1;
@@ -265,6 +270,36 @@
 		hif_ahb_irq_enable(scn, i);
 	}
 
+	/* configure external interrupts */
+	for (i = 0; i < hif_state->hif_num_extgroup; i++) {
+
+		hif_ext_group = &hif_state->hif_ext_group[i];
+		if (hif_ext_group->configured) {
+
+			tasklet_init(&hif_ext_group->intr_tq,
+					hif_ext_grp_tasklet,
+					(unsigned long)hif_ext_group);
+			hif_ext_group->inited = true;
+
+			for (j = 0; j < hif_ext_group->numirq; j++) {
+				irq = platform_get_irq_byname(pdev,
+					ic_irqname[hif_ext_group->irq[j]]);
+
+				ic_irqnum[hif_ext_group->irq[j]] = irq;
+				ret = request_irq(irq,
+					hif_ext_group_ahb_interrupt_handler,
+						IRQF_TRIGGER_RISING, "wlan_ahb",
+						hif_ext_group);
+				if (ret) {
+					dev_err(&pdev->dev,
+						"ath_request_irq failed\n");
+					ret = -1;
+					goto end;
+				}
+			}
+		}
+	}
+
 end:
 	return ret;
 }
@@ -275,6 +310,26 @@
 	return ce_dispatch_interrupt(tasklet_entry->ce_id, tasklet_entry);
 }
 
+irqreturn_t hif_ext_group_ahb_interrupt_handler(int irq, void *context)
+{
+	struct hif_ext_group_entry *hif_ext_group = context;
+	struct HIF_CE_state *hif_state = hif_ext_group->hif_state;
+	struct hif_softc *scn = HIF_GET_SOFTC(hif_state);
+	struct hif_opaque_softc *hif_hdl = GET_HIF_OPAQUE_HDL(scn);
+	uint32_t grp_id = hif_ext_group->grp_id;
+
+	hif_grp_irq_disable(scn, grp_id);
+
+	qdf_atomic_inc(&scn->active_grp_tasklet_cnt);
+
+	if (hif_ext_napi_enabled(hif_hdl, grp_id)) {
+		hif_napi_schedule_grp(hif_hdl, grp_id);
+	} else {
+		tasklet_schedule(&hif_ext_group->intr_tq);
+	}
+
+	return IRQ_HANDLED;
+}
 
 /**
  * hif_target_sync() : ensure the target is ready
@@ -493,9 +548,7 @@
 void hif_ahb_nointrs(struct hif_softc *scn)
 {
 	int i;
-	int irq;
 	struct hif_pci_softc *sc = HIF_GET_PCI_SOFTC(scn);
-	struct platform_device *pdev = (struct platform_device *)sc->pdev;
 	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn);
 
 	if (scn->request_irq_done == false)
@@ -512,8 +565,8 @@
 			free_irq(sc->irq, sc);
 		} else {
 			for (i = 0; i < scn->ce_count; i++) {
-				irq = platform_get_irq_byname(pdev, ic_irqname[HIF_IC_CE0_IRQ_OFFSET + i]);
-				free_irq(irq, sc);
+				free_irq(ic_irqnum[HIF_IC_CE0_IRQ_OFFSET + i],
+						sc);
 			}
 		}
 	}
@@ -592,3 +645,29 @@
 		}
 	}
 }
+
+void hif_ahb_grp_irq_disable(struct hif_softc *scn, uint32_t grp_id)
+{
+	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn);
+	struct hif_ext_group_entry *hif_ext_group;
+	uint32_t i;
+
+	hif_ext_group = &hif_state->hif_ext_group[grp_id];
+
+	for (i = 0; i < hif_ext_group->numirq; i++) {
+		disable_irq(ic_irqnum[hif_ext_group->irq[i]]);
+	}
+}
+
+void hif_ahb_grp_irq_enable(struct hif_softc *scn, uint32_t grp_id)
+{
+	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn);
+	struct hif_ext_group_entry *hif_ext_group;
+	uint32_t i;
+
+	hif_ext_group = &hif_state->hif_ext_group[grp_id];
+
+	for (i = 0; i < hif_ext_group->numirq; i++) {
+		enable_irq(ic_irqnum[hif_ext_group->irq[i]]);
+	}
+}
diff --git a/hif/src/snoc/if_ahb.h b/hif/src/snoc/if_ahb.h
index f5c9081..990d300 100644
--- a/hif/src/snoc/if_ahb.h
+++ b/hif/src/snoc/if_ahb.h
@@ -45,5 +45,9 @@
 #define TCSR_WCSS0_HALTACK 0x52010
 #define TCSR_WCSS1_HALTACK 0x52014
 #define ATH_AHB_RESET_WAIT_MAX 10 /* Ms */
+
+irqreturn_t hif_ahb_interrupt_handler(int irq, void *context);
+irqreturn_t hif_ext_group_ahb_interrupt_handler(int irq, void *context);
+
 #endif
 
diff --git a/hif/src/snoc/if_snoc.c b/hif/src/snoc/if_snoc.c
index 74acfbe..5eb123e 100644
--- a/hif/src/snoc/if_snoc.c
+++ b/hif/src/snoc/if_snoc.c
@@ -55,7 +55,9 @@
 {
 	hif_nointrs(scn);
 	ce_tasklet_kill(scn);
+	hif_grp_tasklet_kill(scn);
 	qdf_atomic_set(&scn->active_tasklet_cnt, 0);
+	qdf_atomic_set(&scn->active_grp_tasklet_cnt, 0);
 }
 
 /**