Commit 88c6216a authored by Ravindra's avatar Ravindra Committed by Luiz Augusto von Dentz
Browse files

Bluetooth: btintel_pcie: Suspend/Resume: Controller doorbell interrupt handling



Due to a hardware bug during suspend/resume, the controller may miss a
doorbell interrupt. To address this, a retry mechanism has been added to
inform the controller before reporting a failure.

Test case:
- run suspend and resume cycles.

Signed-off-by: default avatarRavindra <ravindra@intel.com>
Signed-off-by: default avatarKiran K <kiran.k@intel.com>
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent 1fb0d830
Loading
Loading
Loading
Loading
+71 −42
Original line number Diff line number Diff line
@@ -2524,6 +2524,48 @@ static void btintel_pcie_coredump(struct device *dev)
}
#endif

static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate)
{
	int retry = 0, status;
	u32 dx_intr_timeout_ms = 200;

	do {
		data->gp0_received = false;

		btintel_pcie_wr_sleep_cntrl(data, dxstate);

		status = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
			msecs_to_jiffies(dx_intr_timeout_ms));

		if (status)
			return 0;

		bt_dev_warn(data->hdev,
			   "Timeout (%u ms) on alive interrupt for D%d entry, retry count %d",
			   dx_intr_timeout_ms, dxstate, retry);

		/* clear gp0 cause */
		btintel_pcie_clr_reg_bits(data,
					  BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES,
					  BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0);

		/* A hardware bug may cause the alive interrupt to be missed.
		 * Check if the controller reached the expected state and retry
		 * the operation only if it hasn't.
		 */
		if (dxstate == BTINTEL_PCIE_STATE_D0) {
			if (btintel_pcie_in_d0(data))
				return 0;
		} else {
			if (btintel_pcie_in_d3(data))
				return 0;
		}

	} while (++retry < BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES);

	return -EBUSY;
}

static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg)
{
	struct pci_dev *pdev = to_pci_dev(dev);
@@ -2539,26 +2581,18 @@ static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg)

	data->pm_sx_event = mesg.event;

	data->gp0_received = false;

	start = ktime_get();

	/* Refer: 6.4.11.7 -> Platform power management */
	btintel_pcie_wr_sleep_cntrl(data, dxstate);
	err = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
				 msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS));
	if (err == 0) {
		bt_dev_err(data->hdev,
			   "Timeout (%u ms) on alive interrupt for D3 entry",
			   BTINTEL_DEFAULT_INTR_TIMEOUT_MS);
		return -EBUSY;
	}
	err = btintel_pcie_set_dxstate(data, dxstate);

	if (err)
		return err;

	bt_dev_dbg(data->hdev,
		   "device entered into d3 state from d0 in %lld us",
		   ktime_to_us(ktime_get() - start));

	return 0;
	return err;
}

static int btintel_pcie_suspend(struct device *dev)
@@ -2603,13 +2637,14 @@ static int btintel_pcie_resume(struct device *dev)
	}

	/* Refer: 6.4.11.7 -> Platform power management */
	btintel_pcie_wr_sleep_cntrl(data, BTINTEL_PCIE_STATE_D0);
	err = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
				 msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS));
	err = btintel_pcie_set_dxstate(data, BTINTEL_PCIE_STATE_D0);

	if (err == 0) {
		bt_dev_err(data->hdev,
			   "Timeout (%u ms) on alive interrupt for D0 entry",
			   BTINTEL_DEFAULT_INTR_TIMEOUT_MS);
		bt_dev_dbg(data->hdev,
			   "device entered into d0 state from d3 in %lld us",
			   ktime_to_us(ktime_get() - start));
		return err;
	}

	/* Trigger function level reset if the controller is in error
	 * state during resume() to bring back the controller to
@@ -2630,13 +2665,7 @@ static int btintel_pcie_resume(struct device *dev)
		set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
		btintel_pcie_reset(data->hdev);
	}
		return -EBUSY;
	}

	bt_dev_dbg(data->hdev,
		    "device entered into d0 state from d3 in %lld us",
		     ktime_to_us(ktime_get() - start));
	return 0;
	return err;
}

static const struct dev_pm_ops btintel_pcie_pm_ops = {
+2 −0
Original line number Diff line number Diff line
@@ -158,6 +158,8 @@ enum msix_mbox_int_causes {
/* Default interrupt timeout in msec */
#define BTINTEL_DEFAULT_INTR_TIMEOUT_MS	3000

#define BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES	3

/* The number of descriptors in TX queues */
#define BTINTEL_PCIE_TX_DESCS_COUNT	32