Browse Source

qcacmn: Interrupt handling support for chipset QCA8074

Changes to handle Interrupts for qca8074

Change-Id: I81d4b0722d56388cc7aca08484bd8957258621d1
Acked-by: Venkateswara Swamy Bandaru <[email protected]>
CRs-Fixed: 1042915
Venkateswara Swamy Bandaru 8 years ago
parent
commit
9fd9af065c

+ 4 - 0
hif/inc/hif.h

@@ -62,6 +62,7 @@ typedef void *hif_handle_t;
 #define HIF_TYPE_QCA9984 12
 #define HIF_TYPE_IPQ4019 13
 #define HIF_TYPE_QCA9888 14
+#define HIF_TYPE_QCA8074 15
 
 /* TARGET definition needs to be abstracted in fw common
  * header files, below is the placeholder till WIN codebase
@@ -95,6 +96,9 @@ typedef void *hif_handle_t;
 /* For Adrastea target */
 #define TARGET_TYPE_ADRASTEA     19
 #endif /* CONFIG_WIN */
+#ifndef TARGET_TYPE_QCA8074
+#define TARGET_TYPE_QCA8074   20
+#endif
 
 struct CE_state;
 #define CE_COUNT_MAX 12

+ 4 - 0
hif/src/ce/ce_main.c

@@ -2055,6 +2055,7 @@ QDF_STATUS hif_ce_open(struct hif_softc *hif_sc)
 {
 	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_sc);
 
+	qdf_spinlock_create(&hif_state->irq_reg_lock);
 	qdf_spinlock_create(&hif_state->keep_awake_lock);
 	return QDF_STATUS_SUCCESS;
 }
@@ -2065,6 +2066,9 @@ QDF_STATUS hif_ce_open(struct hif_softc *hif_sc)
  */
 void hif_ce_close(struct hif_softc *hif_sc)
 {
+	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_sc);
+
+	qdf_spinlock_destroy(&hif_state->irq_reg_lock);
 }
 
 /**

+ 1 - 0
hif/src/ce/ce_main.h

@@ -122,6 +122,7 @@ struct HIF_CE_state {
 	bool started;
 	struct ce_tasklet_entry tasklets[CE_COUNT_MAX];
 	qdf_spinlock_t keep_awake_lock;
+	qdf_spinlock_t irq_reg_lock;
 	unsigned int keep_awake_count;
 	bool verified_awake;
 	bool fake_sleep;

+ 1 - 0
hif/src/dispatcher/ahb_api.h

@@ -43,5 +43,6 @@ void hif_ahb_device_reset(struct hif_softc *scn);
 int hif_ahb_enable_radio(struct hif_pci_softc *sc,
 		struct platform_device *pdev,
 		const struct platform_device_id *id);
+int hif_ahb_configure_irq(struct hif_pci_softc *sc);
 
 #endif

+ 6 - 0
hif/src/hif_main.c

@@ -680,6 +680,12 @@ int hif_get_device_type(uint32_t device_id,
 		HIF_INFO(" *********** IPQ4019  *************");
 		break;
 
+	case QCA8074_DEVICE_ID:
+		*hif_type = HIF_TYPE_QCA8074;
+		*target_type = TARGET_TYPE_QCA8074;
+		HIF_INFO(" *********** QCA8074  *************\n");
+		break;
+
 	default:
 		HIF_ERROR("%s: Unsupported device ID!", __func__);
 		ret = -ENODEV;

+ 5 - 0
hif/src/hif_main.h

@@ -91,6 +91,10 @@
 #define QCA9984_DEVICE_ID   (0x0046)
 #define QCA9888_DEVICE_ID   (0x0056)
 #define IPQ4019_DEVICE_ID   (0x12ef)
+#define QCA8074_DEVICE_ID   (0xffff) /* Todo: replace this with
+					actual number once available.
+					currently defining this to 0xffff for
+					emulation purpose */
 
 #define HIF_GET_PCI_SOFTC(scn) ((struct hif_pci_softc *)scn)
 #define HIF_GET_CE_STATE(scn) ((struct HIF_CE_state *)scn)
@@ -127,6 +131,7 @@ struct hif_softc {
 
 	bool recovery;
 	bool notice_send;
+	bool per_ce_irq;
 	uint32_t ce_irq_summary;
 	/* No of copy engines supported */
 	unsigned int ce_count;

+ 7 - 0
hif/src/pcie/if_pci.c

@@ -1952,6 +1952,10 @@ int hif_pci_bus_configure(struct hif_softc *hif_sc)
 		}
 	}
 
+	if (hif_sc->target_info.target_type == TARGET_TYPE_QCA8074) {
+		hif_sc->per_ce_irq = true;
+	}
+
 	status = hif_config_ce(hif_sc);
 	if (status)
 		goto disable_wlan;
@@ -3319,6 +3323,9 @@ int hif_configure_irq(struct hif_softc *scn)
 	case TARGET_TYPE_IPQ4019:
 		ret = hif_ahb_configure_legacy_irq(sc);
 		break;
+	case TARGET_TYPE_QCA8074:
+		ret = hif_ahb_configure_irq(sc);
+		break;
 	default:
 		ret = hif_pci_configure_legacy_irq(sc);
 		break;

+ 11 - 0
hif/src/regtable.c

@@ -103,6 +103,12 @@ void hif_target_register_tbl_attach(struct hif_softc *scn, u32 target_type)
 		scn->target_ce_def = IPQ4019_CE_TARGETdef;
 		break;
 #endif
+#if defined(QCA8074_HEADERS_DEF)
+	case TARGET_TYPE_QCA8074:
+		scn->targetdef = QCA8074_TARGETdef;
+		scn->target_ce_def = QCA8074_CE_TARGETdef;
+		break;
+#endif
 #endif
 
 	default:
@@ -167,6 +173,11 @@ void hif_register_tbl_attach(struct hif_softc *scn, u32 hif_type)
 		scn->hostdef = IPQ4019_HOSTdef;
 		break;
 #endif
+#if defined(QCA8074_HEADERS_DEF)
+	case HIF_TYPE_QCA8074:
+		scn->hostdef = QCA8074_HOSTdef;
+		break;
+#endif
 #endif
 
 	default:

+ 173 - 3
hif/src/snoc/if_ahb.c

@@ -36,6 +36,69 @@
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
 #define IRQF_DISABLED 0x00000020
 #endif
+
+#define HIF_IC_CE0_IRQ_OFFSET 4
+#define HIF_IC_MAX_IRQ 54
+
+/* integrated chip irq names */
+const char *ic_irqname[HIF_IC_MAX_IRQ] = {
+"misc_pulse1",
+"misc_latch",
+"sw_exception",
+"watchdog",
+"ce0",
+"ce1",
+"ce2",
+"ce3",
+"ce4",
+"ce5",
+"ce6",
+"ce7",
+"ce8",
+"ce9",
+"ce10",
+"ce11",
+"ce12",
+"ce13",
+"host2wbm_desc_feed",
+"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",
+};
+
+irqreturn_t hif_ahb_interrupt_handler(int irq, void *context);
 /**
  * hif_disable_isr() - disable isr
  *
@@ -178,6 +241,41 @@ end:
 	return ret;
 }
 
+int hif_ahb_configure_irq(struct hif_pci_softc *sc)
+{
+	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);
+	int irq = 0;
+	int i;
+
+	/* 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]);
+		ret = request_irq(irq ,
+				hif_ahb_interrupt_handler,
+				IRQF_TRIGGER_RISING, ic_irqname[HIF_IC_CE0_IRQ_OFFSET + i],
+				&hif_ce_state->tasklets[i]);
+		if (ret) {
+			dev_err(&pdev->dev, "ath_request_irq failed\n");
+			ret = -1;
+			goto end;
+		}
+		hif_ahb_irq_enable(scn, i);
+	}
+
+end:
+	return ret;
+}
+
+irqreturn_t hif_ahb_interrupt_handler(int irq, void *context)
+{
+	struct ce_tasklet_entry *tasklet_entry = context;
+	return ce_dispatch_interrupt(tasklet_entry->ce_id, tasklet_entry);
+}
+
+
 /**
  * hif_target_sync() : ensure the target is ready
  * @scn: hif control structure
@@ -394,7 +492,33 @@ void hif_ahb_reset_soc(struct hif_softc *hif_ctx)
  */
 void hif_ahb_nointrs(struct hif_softc *scn)
 {
-	hif_pci_nointrs(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)
+		return;
+
+	if (sc->num_msi_intrs > 0) {
+		/* MSI interrupt(s) */
+		for (i = 0; i < sc->num_msi_intrs; i++) {
+			free_irq(sc->irq + i, sc);
+		}
+		sc->num_msi_intrs = 0;
+	} else {
+		if (!scn->per_ce_irq) {
+			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);
+			}
+		}
+	}
+	ce_unregister_irq(hif_state, CE_ALL_BITMAP);
+	scn->request_irq_done = false;
 }
 
 /**
@@ -408,7 +532,31 @@ void hif_ahb_nointrs(struct hif_softc *scn)
  */
 void hif_ahb_irq_enable(struct hif_softc *scn, int ce_id)
 {
-	hif_pci_irq_enable(scn, ce_id);
+	uint32_t regval;
+	uint32_t reg_offset = 0;
+	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn);
+	struct CE_pipe_config *target_ce_conf = &hif_state->target_ce_config[ce_id];
+
+	if (scn->per_ce_irq) {
+		if (target_ce_conf->pipedir & PIPEDIR_OUT) {
+			reg_offset = HOST_IE_ADDRESS;
+			qdf_spin_lock_irqsave(&hif_state->irq_reg_lock);
+			regval = hif_read32_mb(scn->mem + reg_offset);
+			regval |= (1 << ce_id);
+			hif_write32_mb(scn->mem + reg_offset, regval);
+			qdf_spin_unlock_irqrestore(&hif_state->irq_reg_lock);
+		}
+		if (target_ce_conf->pipedir & PIPEDIR_IN) {
+			reg_offset = HOST_IE_ADDRESS_2;
+			qdf_spin_lock_irqsave(&hif_state->irq_reg_lock);
+			regval = hif_read32_mb(scn->mem + reg_offset);
+			regval |= (1 << ce_id);
+			hif_write32_mb(scn->mem + reg_offset, regval);
+			qdf_spin_unlock_irqrestore(&hif_state->irq_reg_lock);
+		}
+	} else {
+		hif_pci_irq_enable(scn, ce_id);
+	}
 }
 
 /**
@@ -420,5 +568,27 @@ void hif_ahb_irq_enable(struct hif_softc *scn, int ce_id)
  */
 void hif_ahb_irq_disable(struct hif_softc *scn, int ce_id)
 {
-
+	uint32_t regval;
+	uint32_t reg_offset = 0;
+	struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn);
+	struct CE_pipe_config *target_ce_conf = &hif_state->target_ce_config[ce_id];
+
+	if (scn->per_ce_irq) {
+		if (target_ce_conf->pipedir & PIPEDIR_OUT) {
+			reg_offset = HOST_IE_ADDRESS;
+			qdf_spin_lock_irqsave(&hif_state->irq_reg_lock);
+			regval = hif_read32_mb(scn->mem + reg_offset);
+			regval &= ~(1 << ce_id);
+			hif_write32_mb(scn->mem + reg_offset, regval);
+			qdf_spin_unlock_irqrestore(&hif_state->irq_reg_lock);
+		}
+		if (target_ce_conf->pipedir & PIPEDIR_IN) {
+			reg_offset = HOST_IE_ADDRESS_2;
+			qdf_spin_lock_irqsave(&hif_state->irq_reg_lock);
+			regval = hif_read32_mb(scn->mem + reg_offset);
+			regval &= ~(1 << ce_id);
+			hif_write32_mb(scn->mem + reg_offset, regval);
+			qdf_spin_unlock_irqrestore(&hif_state->irq_reg_lock);
+		}
+	}
 }