Commit af08571d authored by Haim Dreyfuss's avatar Haim Dreyfuss Committed by Luca Coelho
Browse files

iwlwifi: pcie: support Bz suspend/resume trigger



Instead of using two bits in the doorbell interrupt, the new Bz
devices have a new CSR_IPC_SLEEP_CONTROL register to let drivers
indicate the desired transition before triggering the doorbell
interrupt.

Signed-off-by: default avatarHaim Dreyfuss <haim.dreyfuss@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
Link: https://lore.kernel.org/r/iwlwifi.20211204083238.63f3d150689a.Iaeb6f9b007e81b1a5a02144b0281935e4613cb78@changeid


Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent 87209b7f
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -105,6 +105,10 @@
/* GIO Chicken Bits (PCI Express bus link power management) */
#define CSR_GIO_CHICKEN_BITS    (CSR_BASE+0x100)

#define CSR_IPC_SLEEP_CONTROL	(CSR_BASE + 0x114)
#define CSR_IPC_SLEEP_CONTROL_SUSPEND	0x3
#define CSR_IPC_SLEEP_CONTROL_RESUME	0

/* Doorbell NMI (since Bz) */
#define CSR_DOORBELL_VECTOR	(CSR_BASE + 0x130)
#define CSR_DOORBELL_VECTOR_NMI	BIT(1)
+7 −0
Original line number Diff line number Diff line
@@ -455,6 +455,13 @@ enum {
#define UREG_DOORBELL_TO_ISR6_RESUME	BIT(19)
#define UREG_DOORBELL_TO_ISR6_PNVM	BIT(20)

/*
 * From BZ family driver triggers this bit for suspend and resume
 * The driver should update CSR_IPC_SLEEP_CONTROL before triggering
 * this interrupt with suspend/resume value
 */
#define UREG_DOORBELL_TO_ISR6_SLEEP_CTRL	BIT(31)

#define CNVI_MBOX_C			0xA3400C

#define FSEQ_ERROR_CODE			0xA340C8
+41 −34
Original line number Diff line number Diff line
@@ -1499,33 +1499,54 @@ void iwl_pcie_d3_complete_suspend(struct iwl_trans *trans,
	iwl_pcie_set_pwr(trans, true);
}

static int iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test,
				     bool reset)
static int iwl_pcie_d3_handshake(struct iwl_trans *trans, bool suspend)
{
	int ret;
	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
	int ret;

	if (!reset)
		/* Enable persistence mode to avoid reset */
		iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
			    CSR_HW_IF_CONFIG_REG_PERSIST_MODE);

	if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
	if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) {
		iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
				    UREG_DOORBELL_TO_ISR6_SUSPEND);
				    suspend ? UREG_DOORBELL_TO_ISR6_SUSPEND :
					      UREG_DOORBELL_TO_ISR6_RESUME);
	} else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
		iwl_write32(trans, CSR_IPC_SLEEP_CONTROL,
			    suspend ? CSR_IPC_SLEEP_CONTROL_SUSPEND :
				      CSR_IPC_SLEEP_CONTROL_RESUME);
		iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
				    UREG_DOORBELL_TO_ISR6_SLEEP_CTRL);
	} else {
		return 0;
	}

	ret = wait_event_timeout(trans_pcie->sx_waitq,
				 trans_pcie->sx_complete, 2 * HZ);
		/*
		 * Invalidate it toward resume.
		 */

	/* Invalidate it toward next suspend or resume */
	trans_pcie->sx_complete = false;

	if (!ret) {
			IWL_ERR(trans, "Timeout entering D3\n");
		IWL_ERR(trans, "Timeout %s D3\n",
			suspend ? "entering" : "exiting");
		return -ETIMEDOUT;
	}

	return 0;
}

static int iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test,
				     bool reset)
{
	int ret;

	if (!reset)
		/* Enable persistence mode to avoid reset */
		iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
			    CSR_HW_IF_CONFIG_REG_PERSIST_MODE);

	ret = iwl_pcie_d3_handshake(trans, true);
	if (ret)
		return ret;

	iwl_pcie_d3_complete_suspend(trans, test, reset);

	return 0;
@@ -1542,6 +1563,7 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
	if (test) {
		iwl_enable_interrupts(trans);
		*status = IWL_D3_STATUS_ALIVE;
		ret = 0;
		goto out;
	}

@@ -1590,25 +1612,10 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
		*status = IWL_D3_STATUS_ALIVE;

out:
	if (*status == IWL_D3_STATUS_ALIVE &&
	    trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
		trans_pcie->sx_complete = false;
		iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
				    UREG_DOORBELL_TO_ISR6_RESUME);
	if (*status == IWL_D3_STATUS_ALIVE)
		ret = iwl_pcie_d3_handshake(trans, false);

		ret = wait_event_timeout(trans_pcie->sx_waitq,
					 trans_pcie->sx_complete, 2 * HZ);
		/*
		 * Invalidate it toward next suspend.
		 */
		trans_pcie->sx_complete = false;

		if (!ret) {
			IWL_ERR(trans, "Timeout exiting D3\n");
			return -ETIMEDOUT;
		}
	}
	return 0;
	return ret;
}

static void