Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 960341a5 authored by Tony Truong's avatar Tony Truong Committed by Gerrit - the friendly Code Review server
Browse files

msm: pcie: add support for L1ss timeout



Some PCIe client make use of knowing how long the link
has been in L1 substate (L1ss) so that they can further
optimize their power savings. Provide PCIe clients the
ability to enable and disable L1ss timeout feature.
Clients who register for the L1ss timeout event will receive
a callback after the link has been in L1ss for 100ms. Also,
move PCIe global IRQ to PCIe IPC dump log because l1ss
timeout will trigger often and will flood detailed IPC
logs.

Change-Id: Ifa377fbe9389fe5f0e8c4c999a9abe382cff96e0
Signed-off-by: default avatarTony Truong <truong@codeaurora.org>
parent cafbc393
Loading
Loading
Loading
Loading
+71 −27
Original line number Diff line number Diff line
@@ -64,6 +64,8 @@
#define PCIE20_PARF_INT_ALL_MASK (0x22c)
#define PCIE20_PARF_DEVICE_TYPE (0x1000)
#define PCIE20_PARF_BDF_TO_SID_TABLE_N (0x2000)
#define PCIE20_PARF_L1SUB_AHB_CLK_MAX_TIMER (0x180)
#define PCIE20_PARF_DEBUG_INT_EN (0x190)

#define PCIE20_ELBI_SYS_CTRL (0x04)
#define PCIE20_ELBI_SYS_STTS (0x08)
@@ -185,6 +187,10 @@
#define PCIE20_MSI_CTRL_INTR_STATUS (0x830)
#define PCIE20_MSI_CTRL_MAX (8)

/* Each tick is 19.2 MHz */
#define L1SS_TIMEOUT_US_TO_TICKS(x) (x * 192 / 10)
#define L1SS_TIMEOUT_US (100000)

#ifdef CONFIG_PHYS_ADDR_T_64BIT
#define PCIE_UPPER_ADDR(addr) ((u32)((addr) >> 32))
#else
@@ -692,6 +698,7 @@ struct msm_pcie_dev_t {
	void *ipc_log_dump;
	bool use_19p2mhz_aux_clk;
	bool use_pinctrl;
	bool enable_l1ss_timeout;
	struct pinctrl *pinctrl;
	struct pinctrl_state *pins_default;
	struct pinctrl_state *pins_sleep;
@@ -4040,6 +4047,7 @@ static int msm_pcie_enable(struct msm_pcie_dev_t *dev)

	msm_pcie_write_mask(dev->parf + PCIE20_PARF_INT_ALL_MASK, 0,
				BIT(MSM_PCIE_INT_EVT_LINK_DOWN) |
				BIT(MSM_PCIE_INT_EVT_L1SUB_TIMEOUT) |
				BIT(MSM_PCIE_INT_EVT_AER_LEGACY) |
				BIT(MSM_PCIE_INT_EVT_AER_ERR) |
				BIT(MSM_PCIE_INT_EVT_MSI_0) |
@@ -4051,7 +4059,7 @@ static int msm_pcie_enable(struct msm_pcie_dev_t *dev)
				BIT(MSM_PCIE_INT_EVT_MSI_6) |
				BIT(MSM_PCIE_INT_EVT_MSI_7));

	PCIE_DBG(dev, "PCIe: RC%d: PCIE20_PARF_INT_ALL_MASK: 0x%x\n",
	PCIE_INFO(dev, "PCIe: RC%d: PCIE20_PARF_INT_ALL_MASK: 0x%x\n",
		dev->rc_idx,
		readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_MASK));

@@ -4956,7 +4964,7 @@ static irqreturn_t handle_global_irq(int irq, void *data)

	msm_pcie_write_mask(dev->parf + PCIE20_PARF_INT_ALL_CLEAR, 0, status);

	PCIE_DBG2(dev, "RC%d: Global IRQ %d received: 0x%x\n",
	PCIE_DUMP(dev, "RC%d: Global IRQ %d received: 0x%x\n",
		dev->rc_idx, irq, status);

	for (i = 0; i <= MSM_PCIE_INT_EVT_MAX; i++) {
@@ -4968,6 +4976,10 @@ static irqreturn_t handle_global_irq(int irq, void *data)
					dev->rc_idx);
				handle_linkdown_irq(irq, data);
				break;
			case MSM_PCIE_INT_EVT_L1SUB_TIMEOUT:
				msm_pcie_notify_client(dev,
					MSM_PCIE_EVENT_L1SS_TIMEOUT);
				break;
			case MSM_PCIE_INT_EVT_AER_LEGACY:
				PCIE_DBG(dev,
					"PCIe: RC%d: AER legacy event.\n",
@@ -6471,6 +6483,40 @@ static void msm_pcie_fixup_early(struct pci_dev *dev)
DECLARE_PCI_FIXUP_EARLY(PCIE_VENDOR_ID_QCOM, PCI_ANY_ID,
			msm_pcie_fixup_early);

static void __msm_pcie_l1ss_timeout_disable(struct msm_pcie_dev_t *pcie_dev)
{
	msm_pcie_write_mask(pcie_dev->parf + PCIE20_PARF_DEBUG_INT_EN, BIT(0),
				0);
	writel_relaxed(0, pcie_dev->parf + PCIE20_PARF_L1SUB_AHB_CLK_MAX_TIMER);
}

static void __msm_pcie_l1ss_timeout_enable(struct msm_pcie_dev_t *pcie_dev)
{
	u32 val = BIT(31);

	writel_relaxed(val, pcie_dev->parf +
			PCIE20_PARF_L1SUB_AHB_CLK_MAX_TIMER);

	/* 3 AUX clock cycles so that RESET will sync with timer logic */
	usleep_range(3, 4);

	val |= L1SS_TIMEOUT_US_TO_TICKS(L1SS_TIMEOUT_US);
	writel_relaxed(val, pcie_dev->parf +
			PCIE20_PARF_L1SUB_AHB_CLK_MAX_TIMER);

	/* 1 AUX clock cycle so that CNT_MAX will sync with timer logic */
	usleep_range(1, 2);

	val &= ~BIT(31);
	writel_relaxed(val, pcie_dev->parf +
			PCIE20_PARF_L1SUB_AHB_CLK_MAX_TIMER);

	msm_pcie_write_mask(pcie_dev->parf +
			PCIE20_PARF_DEBUG_INT_EN, 0, BIT(0));

	pcie_dev->enable_l1ss_timeout = true;
}

/* Suspend the PCIe link */
static int msm_pcie_pm_suspend(struct pci_dev *dev,
			void *user, void *data, u32 options)
@@ -6494,6 +6540,9 @@ static int msm_pcie_pm_suspend(struct pci_dev *dev,
		return ret;
	}

	if (pcie_dev->enable_l1ss_timeout)
		__msm_pcie_l1ss_timeout_disable(pcie_dev);

	if (dev && !(options & MSM_PCIE_CONFIG_NO_CFG_RESTORE)
		&& msm_pcie_confirm_linkup(pcie_dev, true, true,
			pcie_dev->conf)) {
@@ -6645,6 +6694,9 @@ static int msm_pcie_pm_resume(struct pci_dev *dev,
			pcie_dev->rc_idx);
	}

	if (pcie_dev->enable_l1ss_timeout)
		__msm_pcie_l1ss_timeout_enable(pcie_dev);

	PCIE_DBG(pcie_dev, "RC%d: exit\n", pcie_dev->rc_idx);

	return ret;
@@ -6698,7 +6750,7 @@ DECLARE_PCI_FIXUP_RESUME_EARLY(PCIE_VENDOR_ID_QCOM, PCI_ANY_ID,
int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user,
			void *data, u32 options)
{
	int i, ret = 0;
	int ret = 0;
	struct pci_dev *dev;
	u32 rc_idx = 0;
	struct msm_pcie_dev_t *pcie_dev;
@@ -6728,30 +6780,6 @@ int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user,
		goto out;
	}

	for (i = 0; i < MAX_DEVICE_NUM; i++) {
		if (!busnr)
			break;
		if (user == pcie_dev->pcidev_table[i].dev) {
			if (busnr == pcie_dev->pcidev_table[i].bdf >> 24)
				break;

			PCIE_ERR(pcie_dev,
				"PCIe: RC%d: bus number %d does not match with the expected value %d\n",
				pcie_dev->rc_idx, busnr,
				pcie_dev->pcidev_table[i].bdf >> 24);
			ret = MSM_PCIE_ERROR;
			goto out;
		}
	}

	if (i == MAX_DEVICE_NUM) {
		PCIE_ERR(pcie_dev,
			"PCIe: RC%d: endpoint device was not found in device table",
			pcie_dev->rc_idx);
		ret = MSM_PCIE_ERROR;
		goto out;
	}

	dev = msm_pcie_dev[rc_idx].dev;

	if (!msm_pcie_dev[rc_idx].drv_ready) {
@@ -6874,6 +6902,22 @@ int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user,
}
EXPORT_SYMBOL(msm_pcie_pm_control);

void msm_pcie_l1ss_timeout_disable(struct pci_dev *pci_dev)
{
	struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(pci_dev->bus);

	__msm_pcie_l1ss_timeout_disable(pcie_dev);
}
EXPORT_SYMBOL(msm_pcie_l1ss_timeout_disable);

void msm_pcie_l1ss_timeout_enable(struct pci_dev *pci_dev)
{
	struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(pci_dev->bus);

	__msm_pcie_l1ss_timeout_enable(pcie_dev);
}
EXPORT_SYMBOL(msm_pcie_l1ss_timeout_enable);

int msm_pcie_register_event(struct msm_pcie_register_event *reg)
{
	int i, ret = 0;
+29 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ enum msm_pcie_event {
	MSM_PCIE_EVENT_LINKDOWN = 0x1,
	MSM_PCIE_EVENT_LINKUP = 0x2,
	MSM_PCIE_EVENT_WAKEUP = 0x4,
	MSM_PCIE_EVENT_L1SS_TIMEOUT = BIT(3),
};

enum msm_pcie_trigger {
@@ -76,6 +77,24 @@ static inline int msm_msi_init(struct device *dev)
int msm_pcie_set_link_bandwidth(struct pci_dev *pci_dev, u16 target_link_speed,
				u16 target_link_width);

/**
 * msm_pcie_l1ss_timeout_disable - disable L1ss timeout feature
 * @pci_dev:	client's pci device structure
 *
 * This function gives PCIe clients the control to disable L1ss timeout
 * feature.
 */
void msm_pcie_l1ss_timeout_disable(struct pci_dev *pci_dev);

/**
 * msm_pcie_l1ss_timeout_enable - enable L1ss timeout feature
 * @pci_dev:	client's pci device structure
 *
 * This function gives PCIe clients the control to enable L1ss timeout
 * feature.
 */
void msm_pcie_l1ss_timeout_enable(struct pci_dev *pci_dev);

/**
 * msm_pcie_pm_control - control the power state of a PCIe link.
 * @pm_opt:	power management operation
@@ -180,6 +199,16 @@ static inline int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr,
	return -ENODEV;
}

static inline int msm_pcie_l1ss_timeout_disable(struct pci_dev *pci_dev)
{
	return -ENODEV;
}

static inline int msm_pcie_l1ss_timeout_enable(struct pci_dev *pci_dev)
{
	return -ENODEV;
}

static inline int msm_pcie_register_event(struct msm_pcie_register_event *reg)
{
	return -ENODEV;