Browse Source

qcacmn: Support force wake request

1. Add hif_force_wake_request API to wake the
mhi and umac before reading/writing the memory region
greater than BAR+4K.
2. Add hif_force_wake_release API to release the
PCIE_PCIE_LOCAL_REG_PCIE_SOC_WAKE_PCIE_LOCAL_REG so the
umac can power collapse again at a later point of time.
3. Add pci stats to dump the force wake status.

Change-Id: Ic6d5463ea0cdb28d9144be61da55e43033b53298
CRs-Fixed: 2478052
Venkata Sharath Chandra Manchala 5 years ago
parent
commit
2b0d3f38d5

+ 0 - 1
dp/wifi3.0/dp_main.c

@@ -3422,7 +3422,6 @@ static struct cdp_pdev *dp_pdev_attach_wifi3(struct cdp_soc_t *txrx_soc,
 	struct wlan_cfg_dp_soc_ctxt *soc_cfg_ctx;
 	int nss_cfg;
 	void *sojourn_buf;
-
 	struct dp_soc *soc = (struct dp_soc *)txrx_soc;
 	struct dp_pdev *pdev = NULL;
 

+ 137 - 109
hal/wifi3.0/hal_api.h

@@ -23,6 +23,22 @@
 #include "qdf_util.h"
 #include "qdf_atomic.h"
 #include "hal_internal.h"
+#include "hif.h"
+#include "hif_io32.h"
+
+/* calculate the register address offset from bar0 of shadow register x */
+#if defined(QCA_WIFI_QCA6390) || defined(QCA_WIFI_QCA6490)
+#define SHADOW_REGISTER_START_ADDRESS_OFFSET 0x000008FC
+#define SHADOW_REGISTER_END_ADDRESS_OFFSET \
+	((SHADOW_REGISTER_START_ADDRESS_OFFSET) + (4 * (MAX_SHADOW_REGISTERS)))
+#define SHADOW_REGISTER(x) ((SHADOW_REGISTER_START_ADDRESS_OFFSET) + (4 * (x)))
+#elif defined(QCA_WIFI_QCA6290)
+#define SHADOW_REGISTER_START_ADDRESS_OFFSET 0x00003024
+#define SHADOW_REGISTER_END_ADDRESS_OFFSET \
+	((SHADOW_REGISTER_START_ADDRESS_OFFSET) + (4 * (MAX_SHADOW_REGISTERS)))
+#define SHADOW_REGISTER(x) ((SHADOW_REGISTER_START_ADDRESS_OFFSET) + (4 * (x)))
+#endif /* QCA_WIFI_QCA6390 || QCA_WIFI_QCA6490 */
+
 #define MAX_UNWINDOWED_ADDRESS 0x80000
 #if defined(QCA_WIFI_QCA6390) || defined(QCA_WIFI_QCA6490) || \
 	defined(QCA_WIFI_QCN9000)
@@ -35,21 +51,12 @@
 #define WINDOW_VALUE_MASK 0x3F
 #define WINDOW_START MAX_UNWINDOWED_ADDRESS
 #define WINDOW_RANGE_MASK 0x7FFFF
-
 /*
  * BAR + 4K is always accessible, any access outside this
  * space requires force wake procedure.
- * OFFSET = 4K - 32 bytes = 0x4063
+ * OFFSET = 4K - 32 bytes = 0xFE0
  */
-#define MAPPED_REF_OFF 0x4063
-
-#ifdef HAL_CONFIG_SLUB_DEBUG_ON
-#define FORCE_WAKE_DELAY_TIMEOUT 100
-#else
-#define FORCE_WAKE_DELAY_TIMEOUT 50
-#endif /* HAL_CONFIG_SLUB_DEBUG_ON */
-
-#define FORCE_WAKE_DELAY_MS 5
+#define MAPPED_REF_OFF 0xFE0
 
 /**
  * hal_ring_desc - opaque handle for DP ring descriptor
@@ -113,16 +120,6 @@ static inline void hal_reg_write_result_check(struct hal_soc *hal_soc,
 #endif
 
 #if !defined(QCA_WIFI_QCA6390) && !defined(QCA_WIFI_QCA6490)
-static inline int hal_force_wake_request(struct hal_soc *soc)
-{
-	return 0;
-}
-
-static inline int hal_force_wake_release(struct hal_soc *soc)
-{
-	return 0;
-}
-
 static inline void hal_lock_reg_access(struct hal_soc *soc,
 				       unsigned long *flags)
 {
@@ -134,37 +131,7 @@ static inline void hal_unlock_reg_access(struct hal_soc *soc,
 {
 	qdf_spin_unlock_irqrestore(&soc->register_access_lock);
 }
-
 #else
-static inline int hal_force_wake_request(struct hal_soc *soc)
-{
-	uint32_t timeout = 0;
-	int ret;
-
-	ret = pld_force_wake_request(soc->qdf_dev->dev);
-	if (ret) {
-		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
-			  "%s: Request send failed %d\n", __func__, ret);
-		return -EINVAL;
-	}
-
-	while (!pld_is_device_awake(soc->qdf_dev->dev) &&
-	       timeout <= FORCE_WAKE_DELAY_TIMEOUT) {
-		mdelay(FORCE_WAKE_DELAY_MS);
-		timeout += FORCE_WAKE_DELAY_MS;
-	}
-
-	if (pld_is_device_awake(soc->qdf_dev->dev) == true)
-		return 0;
-	else
-		return -ETIMEDOUT;
-}
-
-static inline int hal_force_wake_release(struct hal_soc *soc)
-{
-	return pld_force_wake_release(soc->qdf_dev->dev);
-}
-
 static inline void hal_lock_reg_access(struct hal_soc *soc,
 				       unsigned long *flags)
 {
@@ -212,10 +179,30 @@ static inline void hal_select_window(struct hal_soc *hal_soc, uint32_t offset,
 #endif
 
 /**
+ * hal_write32_mb() - Access registers to update configuration
+ * @hal_soc: hal soc handle
+ * @offset: offset address from the BAR
+ * @value: value to write
+ *
+ * Return: None
+ *
+ * Description: Register address space is split below:
+ *     SHADOW REGION       UNWINDOWED REGION    WINDOWED REGION
+ *  |--------------------|-------------------|------------------|
+ * BAR  NO FORCE WAKE  BAR+4K  FORCE WAKE  BAR+512K  FORCE WAKE
+ *
+ * 1. Any access to the shadow region, doesn't need force wake
+ *    and windowing logic to access.
+ * 2. Any access beyond BAR + 4K:
+ *    If init_phase enabled, no force wake is needed and access
+ *    should be based on windowed or unwindowed access.
+ *    If init_phase disabled, force wake is needed and access
+ *    should be based on windowed or unwindowed access.
+ *
  * note1: WINDOW_RANGE_MASK = (1 << WINDOW_SHIFT) -1
  * note2: 1 << WINDOW_SHIFT = MAX_UNWINDOWED_ADDRESS
- * note3: WINDOW_VALUE_MASK = big enough that trying to write past that window
- *				would be a bug
+ * note3: WINDOW_VALUE_MASK = big enough that trying to write past
+ *                            that window would be a bug
  */
 #if !defined(QCA_WIFI_QCA6390) && !defined(QCA_WIFI_QCA6490)
 static inline void hal_write32_mb(struct hal_soc *hal_soc, uint32_t offset,
@@ -248,12 +235,17 @@ static inline void hal_write32_mb(struct hal_soc *hal_soc, uint32_t offset,
 	int ret;
 	unsigned long flags;
 
-	if (offset > MAPPED_REF_OFF) {
-		ret = hal_force_wake_request(hal_soc);
+	/* Region < BAR + 4K can be directly accessed */
+	if (offset < MAPPED_REF_OFF) {
+		qdf_iowrite32(hal_soc->dev_base_addr + offset, value);
+		return;
+	}
+
+	/* Region greater than BAR + 4K */
+	if (!hal_soc->init_phase) {
+		ret = hif_force_wake_request(hal_soc->hif_handle);
 		if (ret) {
-			QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
-				  "%s: Wake up request failed %d\n",
-				  __func__, ret);
+			hal_err("Wake up request failed");
 			QDF_BUG(0);
 			return;
 		}
@@ -278,20 +270,24 @@ static inline void hal_write32_mb(struct hal_soc *hal_soc, uint32_t offset,
 		hal_unlock_reg_access(hal_soc, &flags);
 	}
 
-	if ((offset > MAPPED_REF_OFF) &&
-	    hal_force_wake_release(hal_soc))
-		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
-			  "%s: Wake up release failed\n", __func__);
+	if (!hal_soc->init_phase) {
+		ret = hif_force_wake_release(hal_soc->hif_handle);
+		if (ret) {
+			hal_err("Wake up request failed");
+			QDF_BUG(0);
+			return;
+		}
+	}
 }
-
 #endif
 
 /**
  * hal_write_address_32_mb - write a value to a register
  *
  */
-static inline void hal_write_address_32_mb(struct hal_soc *hal_soc,
-					   void __iomem *addr, uint32_t value)
+static inline
+void hal_write_address_32_mb(struct hal_soc *hal_soc,
+			     void __iomem *addr, uint32_t value)
 {
 	uint32_t offset;
 
@@ -303,7 +299,29 @@ static inline void hal_write_address_32_mb(struct hal_soc *hal_soc,
 }
 
 #if !defined(QCA_WIFI_QCA6390) && !defined(QCA_WIFI_QCA6490)
-static inline uint32_t hal_read32_mb(struct hal_soc *hal_soc, uint32_t offset)
+/**
+ * hal_read32_mb() - Access registers to read configuration
+ * @hal_soc: hal soc handle
+ * @offset: offset address from the BAR
+ * @value: value to write
+ *
+ * Description: Register address space is split below:
+ *     SHADOW REGION       UNWINDOWED REGION    WINDOWED REGION
+ *  |--------------------|-------------------|------------------|
+ * BAR  NO FORCE WAKE  BAR+4K  FORCE WAKE  BAR+512K  FORCE WAKE
+ *
+ * 1. Any access to the shadow region, doesn't need force wake
+ *    and windowing logic to access.
+ * 2. Any access beyond BAR + 4K:
+ *    If init_phase enabled, no force wake is needed and access
+ *    should be based on windowed or unwindowed access.
+ *    If init_phase disabled, force wake is needed and access
+ *    should be based on windowed or unwindowed access.
+ *
+ * Return: < 0 for failure/>= 0 for success
+ */
+static inline
+uint32_t hal_read32_mb(struct hal_soc *hal_soc, uint32_t offset)
 {
 	uint32_t ret;
 	unsigned long flags;
@@ -321,61 +339,56 @@ static inline uint32_t hal_read32_mb(struct hal_soc *hal_soc, uint32_t offset)
 
 	return ret;
 }
-
-/**
- * hal_read_address_32_mb() - Read 32-bit value from the register
- * @soc: soc handle
- * @addr: register address to read
- *
- * Return: 32-bit value
- */
-static inline uint32_t hal_read_address_32_mb(struct hal_soc *soc,
-					      void __iomem *addr)
-{
-	uint32_t offset;
-	uint32_t ret;
-
-	if (!soc->use_register_windowing)
-		return qdf_ioread32(addr);
-
-	offset = addr - soc->dev_base_addr;
-	ret = hal_read32_mb(soc, offset);
-	return ret;
-}
 #else
-static inline uint32_t hal_read32_mb(struct hal_soc *hal_soc, uint32_t offset)
+static
+uint32_t hal_read32_mb(struct hal_soc *hal_soc, uint32_t offset)
 {
 	uint32_t ret;
 	unsigned long flags;
 
-	if ((offset > MAPPED_REF_OFF) &&
-	    hal_force_wake_request(hal_soc)) {
-		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
-			  "%s: Wake up request failed\n", __func__);
-		return -EINVAL;
+	/* Region < BAR + 4K can be directly accessed */
+	if (offset < MAPPED_REF_OFF)
+		return qdf_ioread32(hal_soc->dev_base_addr + offset);
+
+	if ((!hal_soc->init_phase) &&
+	    hif_force_wake_request(hal_soc->hif_handle)) {
+		hal_err("Wake up request failed");
+		QDF_BUG(0);
+		return 0;
 	}
 
 	if (!hal_soc->use_register_windowing ||
 	    offset < MAX_UNWINDOWED_ADDRESS) {
-		return qdf_ioread32(hal_soc->dev_base_addr + offset);
+		ret = qdf_ioread32(hal_soc->dev_base_addr + offset);
+	} else {
+		hal_lock_reg_access(hal_soc, &flags);
+		hal_select_window(hal_soc, offset, false);
+		ret = qdf_ioread32(hal_soc->dev_base_addr + WINDOW_START +
+			       (offset & WINDOW_RANGE_MASK));
+		hal_unlock_reg_access(hal_soc, &flags);
 	}
 
-	hal_lock_reg_access(hal_soc, &flags);
-	hal_select_window(hal_soc, offset, false);
-	ret = qdf_ioread32(hal_soc->dev_base_addr + WINDOW_START +
-		       (offset & WINDOW_RANGE_MASK));
-	hal_unlock_reg_access(hal_soc, &flags);
-
-	if ((offset > MAPPED_REF_OFF) &&
-	    hal_force_wake_release(hal_soc))
-		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
-			  "%s: Wake up release failed\n", __func__);
+	if ((!hal_soc->init_phase) &&
+	    hif_force_wake_release(hal_soc->hif_handle)) {
+		hal_err("Wake up release failed");
+		QDF_BUG(0);
+		return 0;
+	}
 
 	return ret;
 }
+#endif
 
-static inline uint32_t hal_read_address_32_mb(struct hal_soc *soc,
-					      void __iomem *addr)
+/**
+ * hal_read_address_32_mb() - Read 32-bit value from the register
+ * @soc: soc handle
+ * @addr: register address to read
+ *
+ * Return: 32-bit value
+ */
+static inline
+uint32_t hal_read_address_32_mb(struct hal_soc *soc,
+				void __iomem *addr)
 {
 	uint32_t offset;
 	uint32_t ret;
@@ -387,9 +400,6 @@ static inline uint32_t hal_read_address_32_mb(struct hal_soc *soc,
 	ret = hal_read32_mb(soc, offset);
 	return ret;
 }
-#endif
-
-#include "hif_io32.h"
 
 /**
  * hal_attach - Initialize HAL layer
@@ -455,6 +465,24 @@ enum hal_ring_type {
 #define PN_SIZE_48 1
 #define PN_SIZE_128 2
 
+#ifdef FORCE_WAKE
+/**
+ * hal_set_init_phase() - Indicate initialization of
+ *                        datapath rings
+ * @soc: hal_soc handle
+ * @init_phase: flag to indicate datapath rings
+ *              initialization status
+ *
+ * Return: None
+ */
+void hal_set_init_phase(hal_soc_handle_t soc, bool init_phase);
+#else
+static inline
+void hal_set_init_phase(hal_soc_handle_t soc, bool init_phase)
+{
+}
+#endif /* FORCE_WAKE */
+
 /**
  * hal_srng_get_entrysize - Returns size of ring entry in bytes. Should be
  * used by callers for calculating the size of memory to be allocated before

+ 0 - 7
hal/wifi3.0/hal_hw_headers.h

@@ -83,13 +83,6 @@
 #define HAL_SRNG_REO_ALTERNATE_SELECT 0x7
 #define HAL_NON_QOS_TID 16
 
-/* calculate the register address offset from bar0 of shadow register x */
-#if defined(QCA_WIFI_QCA6390) || defined(QCA_WIFI_QCA6490)
-#define SHADOW_REGISTER(x) (0x000008FC + (4 * (x)))
-#else
-#define SHADOW_REGISTER(x) (0x00003024 + (4 * (x)))
-#endif
-
 /* TODO: Check if the following can be provided directly by HW headers */
 #define SRNG_LOOP_CNT_MASK REO_DESTINATION_RING_15_LOOPING_COUNT_MASK
 #define SRNG_LOOP_CNT_LSB REO_DESTINATION_RING_15_LOOPING_COUNT_LSB

+ 3 - 0
hal/wifi3.0/hal_internal.h

@@ -502,6 +502,9 @@ struct hal_soc {
 	struct hal_hw_srng_config *hw_srng_table;
 	int32_t *hal_hw_reg_offset;
 	struct hal_hw_txrx_ops *ops;
+
+	/* Indicate srngs initialization */
+	bool init_phase;
 };
 
 void hal_qca6490_attach(struct hal_soc *hal_soc);

+ 14 - 0
hal/wifi3.0/hal_srng.c

@@ -355,6 +355,11 @@ void *hal_attach(struct hif_opaque_softc *hif_handle, qdf_device_t qdf_dev)
 	hal->target_type = hal_get_target_type(hal_soc_to_hal_soc_handle(hal));
 
 	hal_target_based_configure(hal);
+	/**
+	 * Indicate Initialization of srngs to avoid force wake
+	 * as umac power collapse is not enabled yet
+	 */
+	hal->init_phase = true;
 
 	return (void *)hal;
 
@@ -849,3 +854,12 @@ extern void hal_get_srng_params(hal_soc_handle_t hal_soc_hdl,
 		ring_params->hwreg_base[i] = srng->hwreg_base[i];
 }
 qdf_export_symbol(hal_get_srng_params);
+
+#ifdef FORCE_WAKE
+void hal_set_init_phase(hal_soc_handle_t soc, bool init_phase)
+{
+	struct hal_soc *hal_soc = (struct hal_soc *)soc;
+
+	hal_soc->init_phase = init_phase;
+}
+#endif /* FORCE_WAKE */

+ 64 - 0
hif/inc/hif.h

@@ -1128,6 +1128,50 @@ void hif_clear_napi_stats(struct hif_opaque_softc *hif_ctx);
 }
 #endif
 
+#ifdef FORCE_WAKE
+/**
+ * hif_force_wake_request() - Function to wake from power collapse
+ * @handle: HIF opaque handle
+ *
+ * Description: API to check if the device is awake or not before
+ * read/write to BAR + 4K registers. If device is awake return
+ * success otherwise write '1' to
+ * PCIE_PCIE_LOCAL_REG_PCIE_SOC_WAKE_PCIE_LOCAL_REG which will interrupt
+ * the device and does wakeup the PCI and MHI within 50ms
+ * and then the device writes a value to
+ * PCIE_SOC_PCIE_REG_PCIE_SCRATCH_0_SOC_PCIE_REG to complete the
+ * handshake process to let the host know the device is awake.
+ *
+ * Return: zero - success/non-zero - failure
+ */
+int hif_force_wake_request(struct hif_opaque_softc *handle);
+
+/**
+ * hif_force_wake_release() - API to release/reset the SOC wake register
+ * from interrupting the device.
+ * @handle: HIF opaque handle
+ *
+ * Description: API to set the
+ * PCIE_PCIE_LOCAL_REG_PCIE_SOC_WAKE_PCIE_LOCAL_REG to '0'
+ * to release the interrupt line.
+ *
+ * Return: zero - success/non-zero - failure
+ */
+int hif_force_wake_release(struct hif_opaque_softc *handle);
+#else
+static inline
+int hif_force_wake_request(struct hif_opaque_softc *handle)
+{
+	return 0;
+}
+
+static inline
+int hif_force_wake_release(struct hif_opaque_softc *handle)
+{
+	return 0;
+}
+#endif /* FORCE_WAKE */
+
 void *hif_get_dev_ba(struct hif_opaque_softc *hif_handle);
 
 /**
@@ -1224,4 +1268,24 @@ hif_softc_to_hif_opaque_softc(struct hif_softc *hif_handle)
 {
 	return (struct hif_opaque_softc *)hif_handle;
 }
+
+#ifdef FORCE_WAKE
+/**
+ * hif_srng_init_phase(): Indicate srng initialization phase
+ * to avoid force wake as UMAC power collapse is not yet
+ * enabled
+ * @hif_ctx: hif opaque handle
+ * @init_phase: initialization phase
+ *
+ * Return:  None
+ */
+void hif_srng_init_phase(struct hif_opaque_softc *hif_ctx,
+			 bool init_phase);
+#else
+static inline
+void hif_srng_init_phase(struct hif_opaque_softc *hif_ctx,
+			 bool init_phase)
+{
+}
+#endif /* FORCE_WAKE */
 #endif /* _HIF_H_ */

+ 4 - 7
hif/src/hif_io32.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2019 The Linux Foundation. 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
@@ -28,10 +28,9 @@
 static inline
 void hif_write32_mb_reg_window(void *sc,
 			       void __iomem *addr, uint32_t value);
-
-static inline uint32_t hif_read32_mb_reg_window(void *sc,
-						void __iomem *addr);
-
+static inline
+uint32_t hif_read32_mb_reg_window(void *sc,
+				  void __iomem *addr);
 #define hif_read32_mb(scn, addr) \
 	hif_read32_mb_reg_window((void *)scn, \
 				 (void __iomem *)addr)
@@ -40,11 +39,9 @@ static inline uint32_t hif_read32_mb_reg_window(void *sc,
 				  (void __iomem *)addr, value)
 
 #else
-
 #define hif_read32_mb(scn, addr)         ioread32((void __iomem *)addr)
 #define hif_write32_mb(scn, addr, value) \
 	iowrite32((u32)(value), (void __iomem *)(addr))
-
 #endif
 
 #define Q_TARGET_ACCESS_BEGIN(scn) \

+ 11 - 0
hif/src/hif_main.c

@@ -77,6 +77,17 @@ void *hif_get_targetdef(struct hif_opaque_softc *hif_ctx)
 	return scn->targetdef;
 }
 
+#ifdef FORCE_WAKE
+void hif_srng_init_phase(struct hif_opaque_softc *hif_ctx,
+			 bool init_phase)
+{
+	struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx);
+
+	if (ce_srng_based(scn))
+		hal_set_init_phase(scn->hal_soc, init_phase);
+}
+#endif /* FORCE_WAKE */
+
 /**
  * hif_vote_link_down(): unvote for link up
  *

+ 5 - 0
hif/src/hif_main.h

@@ -45,6 +45,11 @@
 
 #define HIF_MAX_BUDGET 0xFFFF
 
+#define HIF_STATS_INC(_handle, _field, _delta) \
+{ \
+	(_handle)->stats._field += _delta; \
+}
+
 /*
  * This macro implementation is exposed for efficiency only.
  * The implementation may change and callers should

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

@@ -1396,6 +1396,8 @@ void hif_pci_display_stats(struct hif_softc *hif_ctx)
 		return;
 	}
 	hif_display_ce_stats(&pci_ctx->ce_sc);
+
+	hif_print_pci_stats(pci_ctx);
 }
 
 void hif_pci_clear_stats(struct hif_softc *hif_ctx)
@@ -4627,3 +4629,110 @@ bool hif_pci_needs_bmi(struct hif_softc *scn)
 {
 	return !ce_srng_based(scn);
 }
+
+#ifdef FORCE_WAKE
+int hif_force_wake_request(struct hif_opaque_softc *hif_handle)
+{
+	uint32_t timeout = 0, value;
+	struct hif_softc *scn = (struct hif_softc *)hif_handle;
+	struct hif_pci_softc *pci_scn = HIF_GET_PCI_SOFTC(scn);
+
+	if (pld_force_wake_request(scn->qdf_dev->dev)) {
+		hif_err("force wake request send failed");
+		return -EINVAL;
+	}
+
+	HIF_STATS_INC(pci_scn, mhi_force_wake_request_vote, 1);
+	while (!pld_is_device_awake(scn->qdf_dev->dev) &&
+	       timeout <= FORCE_WAKE_DELAY_TIMEOUT_MS) {
+		qdf_mdelay(FORCE_WAKE_DELAY_MS);
+		timeout += FORCE_WAKE_DELAY_MS;
+	}
+
+	if (pld_is_device_awake(scn->qdf_dev->dev) <= 0) {
+		hif_err("Unable to wake up mhi");
+		HIF_STATS_INC(pci_scn, mhi_force_wake_failure, 1);
+		return -EINVAL;
+	}
+	HIF_STATS_INC(pci_scn, mhi_force_wake_success, 1);
+	hif_write32_mb(scn,
+		       scn->mem +
+		       PCIE_SOC_PCIE_REG_PCIE_SCRATCH_0_SOC_PCIE_REG,
+		       0);
+	hif_write32_mb(scn,
+		       scn->mem +
+		       PCIE_PCIE_LOCAL_REG_PCIE_SOC_WAKE_PCIE_LOCAL_REG,
+		       1);
+
+	HIF_STATS_INC(pci_scn, soc_force_wake_register_write_success, 1);
+	/*
+	 * do not reset the timeout
+	 * total_wake_time = MHI_WAKE_TIME + PCI_WAKE_TIME < 50 ms
+	 */
+	do {
+		value =
+		hif_read32_mb(scn,
+			      scn->mem +
+			      PCIE_SOC_PCIE_REG_PCIE_SCRATCH_0_SOC_PCIE_REG);
+		if (value)
+			break;
+		qdf_mdelay(FORCE_WAKE_DELAY_MS);
+		timeout += FORCE_WAKE_DELAY_MS;
+	} while (timeout <= FORCE_WAKE_DELAY_TIMEOUT_MS);
+
+	if (!value) {
+		hif_err("failed handshake mechanism");
+		HIF_STATS_INC(pci_scn, soc_force_wake_failure, 1);
+		return -ETIMEDOUT;
+	}
+
+	HIF_STATS_INC(pci_scn, soc_force_wake_success, 1);
+
+	return 0;
+}
+
+int hif_force_wake_release(struct hif_opaque_softc *hif_handle)
+{
+	int ret;
+	struct hif_softc *scn = (struct hif_softc *)hif_handle;
+	struct hif_pci_softc *pci_scn = HIF_GET_PCI_SOFTC(scn);
+
+	ret = pld_force_wake_release(scn->qdf_dev->dev);
+	if (ret) {
+		hif_err("force wake release failure");
+		HIF_STATS_INC(pci_scn, mhi_force_wake_release_failure, 1);
+		return ret;
+	}
+
+	HIF_STATS_INC(pci_scn, mhi_force_wake_release_success, 1);
+	hif_write32_mb(scn,
+		       scn->mem +
+		       PCIE_PCIE_LOCAL_REG_PCIE_SOC_WAKE_PCIE_LOCAL_REG,
+		       0);
+	HIF_STATS_INC(pci_scn, soc_force_wake_release_success, 1);
+	return 0;
+}
+
+void hif_print_pci_stats(struct hif_pci_softc *pci_handle)
+{
+	hif_debug("mhi_force_wake_request_vote: %d",
+		  pci_handle->stats.mhi_force_wake_request_vote);
+	hif_debug("mhi_force_wake_failure: %d",
+		  pci_handle->stats.mhi_force_wake_failure);
+	hif_debug("mhi_force_wake_success: %d",
+		  pci_handle->stats.mhi_force_wake_success);
+	hif_debug("soc_force_wake_register_write_success: %d",
+		  pci_handle->stats.soc_force_wake_register_write_success);
+	hif_debug("soc_force_wake_failure: %d",
+		  pci_handle->stats.soc_force_wake_failure);
+	hif_debug("soc_force_wake_success: %d",
+		  pci_handle->stats.soc_force_wake_success);
+	hif_debug("mhi_force_wake_release_failure: %d",
+		  pci_handle->stats.mhi_force_wake_release_failure);
+	hif_debug("mhi_force_wake_release_success: %d",
+		  pci_handle->stats.mhi_force_wake_release_success);
+	hif_debug("oc_force_wake_release_success: %d",
+		  pci_handle->stats.soc_force_wake_release_success);
+}
+#endif /* FORCE_WAKE */
+

+ 54 - 0
hif/src/pcie/if_pci.h

@@ -29,6 +29,21 @@
 #include "cepci.h"
 #include "ce_main.h"
 
+#ifdef FORCE_WAKE
+/* Register to wake the UMAC from power collapse */
+#define PCIE_SOC_PCIE_REG_PCIE_SCRATCH_0_SOC_PCIE_REG 0x4040
+/* Register used for handshake mechanism to validate UMAC is awake */
+#define PCIE_PCIE_LOCAL_REG_PCIE_SOC_WAKE_PCIE_LOCAL_REG 0x3004
+/* Timeout duration to validate UMAC wake status */
+#ifdef HAL_CONFIG_SLUB_DEBUG_ON
+#define FORCE_WAKE_DELAY_TIMEOUT_MS 100
+#else
+#define FORCE_WAKE_DELAY_TIMEOUT_MS 50
+#endif /* HAL_CONFIG_SLUB_DEBUG_ON */
+/* Validate UMAC status every 5ms */
+#define FORCE_WAKE_DELAY_MS 5
+#endif /* FORCE_WAKE */
+
 #ifdef QCA_HIF_HIA_EXTND
 extern int32_t frac, intval, ar900b_20_targ_clk, qca9888_20_targ_clk;
 #endif
@@ -106,6 +121,29 @@ struct hif_msi_info {
 	OS_DMA_MEM_CONTEXT(dmacontext);
 };
 
+/**
+ * struct hif_pci_stats - Account for hif pci based statistics
+ * @mhi_force_wake_request_vote: vote for mhi
+ * @mhi_force_wake_failure: mhi force wake failure
+ * @mhi_force_wake_success: mhi force wake success
+ * @soc_force_wake_register_write_success: write to soc wake
+ * @soc_force_wake_failure: soc force wake failure
+ * @soc_force_wake_success: soc force wake success
+ * @mhi_force_wake_release_success: mhi force wake release success
+ * @soc_force_wake_release_success: soc force wake release
+ */
+struct hif_pci_stats {
+	uint32_t mhi_force_wake_request_vote;
+	uint32_t mhi_force_wake_failure;
+	uint32_t mhi_force_wake_success;
+	uint32_t soc_force_wake_register_write_success;
+	uint32_t soc_force_wake_failure;
+	uint32_t soc_force_wake_success;
+	uint32_t mhi_force_wake_release_failure;
+	uint32_t mhi_force_wake_release_success;
+	uint32_t soc_force_wake_release_success;
+};
+
 struct hif_pci_softc {
 	struct HIF_CE_state ce_sc;
 	void __iomem *mem;      /* PCI address. */
@@ -152,6 +190,7 @@ struct hif_pci_softc {
 	void (*hif_pci_deinit)(struct hif_pci_softc *sc);
 	void (*hif_pci_get_soc_info)(struct hif_pci_softc *sc,
 				     struct device *dev);
+	struct hif_pci_stats stats;
 };
 
 bool hif_pci_targ_is_present(struct hif_softc *scn, void *__iomem *mem);
@@ -196,6 +235,21 @@ int hif_pci_addr_in_boundary(struct hif_softc *scn, uint32_t offset);
 #define OL_ATH_TX_DRAIN_WAIT_CNT       60
 #endif
 
+#ifdef FORCE_WAKE
+/**
+ * hif_print_pci_stats() - Display HIF PCI stats
+ * @hif_ctx - HIF pci handle
+ *
+ * Return: None
+ */
+void hif_print_pci_stats(struct hif_pci_softc *pci_scn);
+#else
+static inline
+void hif_print_pci_stats(struct hif_pci_softc *pci_scn)
+{
+}
+#endif /* FORCE_WAKE */
+
 #ifdef FEATURE_RUNTIME_PM
 #include <linux/pm_runtime.h>