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

Commit 9f31a0b2 authored by Hemant Kumar's avatar Hemant Kumar
Browse files

pci: msm: Enable support for PCI Express ASPM control driver



Driver is currently using internal APIs to support L0s, ASPM L1
and ASPM L1ss. PCIE ASPM control driver is compliant to PCIE
specification. Hence use this driver in powersupersave mode.
Driver reads the capability of link partner to enable/disable
L0s, ASPM L1 and ASPM L1ss. In order to enable/disable link
power states link, capability register of root complex is modified
based on device tree property. Also add t_power_on value and scale
from device tree which is used by ASPM control driver to calculate
the L1.2 threshold value.

Change-Id: Ibb69c68659cd6b03011d507fab4eeed6ee88ddc4
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
parent d015e917
Loading
Loading
Loading
Loading
+146 −84
Original line number Diff line number Diff line
@@ -59,10 +59,13 @@
#define PCIE_GEN3_EQ_FMDC_T_MIN_PHASE23_MASK (0x1f)

#define PCIE_GEN3_MISC_CONTROL (0x08bc)
#define DBI_RO_WR_EN (BIT(0))

#define PCIE_PL_16GT_CAP (0x168)

#define PCIE20_PARF_SYS_CTRL (0x00)
#define CORE_CLK_2AUX_CLK_MUX_DIS (BIT(3))

#define PCIE20_PARF_PM_CTRL (0x20)
#define PCIE20_PARF_PM_STTS (0x24)
#define PCIE20_PARF_PHY_CTRL (0x40)
@@ -103,6 +106,10 @@
#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10)
#define PCIE_CAP_DLL_ACTIVE BIT(29)

#define PCIE20_CAP_LINKCAP (PCIE20_CAP + 0xc)
#define PCIE_CAP_ASPM_L0S (BIT(10))
#define PCIE_CAP_ASPM_L1 (BIT(11))

#define PCIE20_COMMAND_STATUS (0x04)
#define PCIE20_HEADER_TYPE (0x0c)
#define PCIE20_BRIDGE_CTRL (0x3c)
@@ -793,6 +800,8 @@ struct msm_pcie_dev_t {
	bool l1_2_aspm_supported;
	uint32_t l1_2_th_scale;
	uint32_t l1_2_th_value;
	uint32_t t_pwr_on_val;
	uint32_t t_pwr_on_scale;
	bool common_clk_en;
	bool clk_power_manage_en;
	bool aux_clk_sync;
@@ -1287,7 +1296,7 @@ static void msm_pcie_config_l1ss_enable_all(struct msm_pcie_dev_t *dev);

static void msm_pcie_check_l1ss_support_all(struct msm_pcie_dev_t *dev);

static void msm_pcie_config_link_pm(struct msm_pcie_dev_t *dev, bool enable);
static void msm_pcie_config_aspm(struct msm_pcie_dev_t *dev);

static u32 msm_pcie_reg_copy(struct msm_pcie_dev_t *pcie_dev,
		u8 *buf, u32 size, void __iomem *base,
@@ -1665,6 +1674,8 @@ static void msm_pcie_show_status(struct msm_pcie_dev_t *dev)
		dev->l1_2_th_scale);
	PCIE_DBG_FS(dev, "l1_2_th_value is %d\n",
		dev->l1_2_th_value);
	PCIE_DBG_FS(dev, "t_pwr_on_scale is %u\n", dev->t_pwr_on_scale);
	PCIE_DBG_FS(dev, "t_pwr_on_val is %u\n", dev->t_pwr_on_val);
	PCIE_DBG_FS(dev, "common_clk_en is %d\n",
		dev->common_clk_en);
	PCIE_DBG_FS(dev, "clk_power_manage_en is %d\n",
@@ -3653,6 +3664,126 @@ static void msm_pcie_config_controller_phy(struct msm_pcie_dev_t *pcie_dev)
	}
}

static u32 msm_pcie_find_ext_capability(struct msm_pcie_dev_t *pcie_dev,
						u32 cap_id)
{
	int total;
	u32 header;
	int pos = PCI_CFG_SPACE_SIZE;

	total = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8;

	header = readl_relaxed(pcie_dev->dm_core + PCIE_EXT_CAP_OFFSET);
	if (!header)
		return 0;

	while (total-- > 0) {
		if (PCI_EXT_CAP_ID(header) == cap_id && pos != 0)
			return pos;

		pos = PCI_EXT_CAP_NEXT(header);
		if (pos < PCI_CFG_SPACE_SIZE)
			break;

		header = readl_relaxed(pcie_dev->dm_core + pos);
	}

	return 0;
}

static void msm_pcie_config_aspm(struct msm_pcie_dev_t *pcie_dev)
{
	u32 reg;
	u32 l1ss_cap_id_offset, l1ss_cap_offset;

	/* enable write access to RO register */
	msm_pcie_write_mask(pcie_dev->dm_core + PCIE_GEN3_MISC_CONTROL, 0,
			    DBI_RO_WR_EN);

	reg = readl_relaxed(pcie_dev->dm_core + PCIE20_CAP_LINKCAP);
	reg >>= 10;

	if (pcie_dev->l0s_supported) {
		if (!(reg & PCI_EXP_LNKCTL_ASPM_L0S)) {
			PCIE_ERR(pcie_dev, "PCIe: RC%d: L0s not supported\n",
				 pcie_dev->rc_idx);
			pcie_dev->l0s_supported = false;
		}
	} else {
		if (reg & PCI_EXP_LNKCTL_ASPM_L0S)
			msm_pcie_write_mask(pcie_dev->dm_core + PCIE20_CAP_LINKCAP,
					    PCIE_CAP_ASPM_L0S, 0);
	}

	if (pcie_dev->l1_supported) {
		if (!(reg & PCI_EXP_LNKCTL_ASPM_L1)) {
			PCIE_ERR(pcie_dev, "PCIe: RC%d: L1 not supported\n",
				 pcie_dev->rc_idx);
			pcie_dev->l1_supported = false;
			goto done;
		}
	} else {
		if (reg & PCI_EXP_LNKCTL_ASPM_L1)
			msm_pcie_write_mask(
					pcie_dev->dm_core + PCIE20_CAP_LINKCAP,
					PCIE_CAP_ASPM_L1, 0);
	}

	l1ss_cap_id_offset = msm_pcie_find_ext_capability(pcie_dev,
							  PCI_EXT_CAP_ID_L1SS);
	if (!l1ss_cap_id_offset) {
		PCIE_DBG(pcie_dev,
			 "PCIe: RC%d: L1ss capability register not found\n",
			 pcie_dev->rc_idx);
		pcie_dev->l1ss_supported = false;
		goto done;
	}

	l1ss_cap_offset = l1ss_cap_id_offset + PCI_L1SS_CAP;

	reg = readl_relaxed(pcie_dev->dm_core + l1ss_cap_offset);
	if (pcie_dev->l1ss_supported) {
		if (!(reg & PCI_L1SS_CAP_PCIPM_L1_2) &&
		    !(reg & PCI_L1SS_CAP_PCIPM_L1_1) &&
		    !(reg & PCI_L1SS_CAP_ASPM_L1_2) &&
		    !(reg & PCI_L1SS_CAP_ASPM_L1_1)) {
			PCIE_ERR(pcie_dev, "PCIe: RC%d: L1ss not supported\n",
				 pcie_dev->rc_idx);
			pcie_dev->l1ss_supported = false;
			goto done;
		}

		if (pcie_dev->t_pwr_on_scale <= 0x3 &&
		    pcie_dev->t_pwr_on_val <= 0x1f) {
			reg |= (pcie_dev->t_pwr_on_scale << 16) |
				pcie_dev->t_pwr_on_val << 19;
			writel_relaxed(reg,
				       pcie_dev->dm_core + l1ss_cap_offset);
		}

		/* Enable AUX clk and Core clk async for L1SS */
		if (!pcie_dev->aux_clk_sync)
			msm_pcie_write_mask(pcie_dev->parf + PCIE20_PARF_SYS_CTRL,
					    CORE_CLK_2AUX_CLK_MUX_DIS, 0);
	} else {
		msm_pcie_write_mask(pcie_dev->dm_core + l1ss_cap_offset,
				    PCI_L1SS_CTL1_PCIPM_L1_1 |
				    PCI_L1SS_CTL1_PCIPM_L1_2 |
				    PCI_L1SS_CTL1_ASPM_L1_1 |
				    PCI_L1SS_CTL1_ASPM_L1_2, 0);

		/* Disable AUX clk and Core Clock async - L1SS disabled */
		if (!pcie_dev->aux_clk_sync)
			msm_pcie_write_mask(pcie_dev->parf + PCIE20_PARF_SYS_CTRL, 0,
					    CORE_CLK_2AUX_CLK_MUX_DIS);
	}

done:
	/* disable write access to RO register */
	msm_pcie_write_mask(pcie_dev->dm_core + PCIE_GEN3_MISC_CONTROL,
			    DBI_RO_WR_EN, 0);
}

static void msm_pcie_config_controller(struct msm_pcie_dev_t *dev)
{
	PCIE_DBG(dev, "RC%d\n", dev->rc_idx);
@@ -3705,6 +3836,8 @@ static void msm_pcie_config_controller(struct msm_pcie_dev_t *dev)
		PCIE_DBG(dev, "RC's PCIE20_CAP_DEVCTRLSTATUS:0x%x\n",
			readl_relaxed(dev->dm_core + PCIE20_CAP_DEVCTRLSTATUS));
	}

	msm_pcie_config_aspm(dev);
}

static int msm_pcie_get_clk(struct msm_pcie_dev_t *pcie_dev)
@@ -4611,6 +4744,9 @@ static int msm_pcie_enable(struct msm_pcie_dev_t *dev)

	ep_up_timeout = jiffies + usecs_to_jiffies(EP_UP_TIMEOUT_US);

	msm_pcie_config_sid(dev);
	msm_pcie_config_controller(dev);

	ret = msm_pcie_link_train(dev);
	if (ret)
		goto link_fail;
@@ -4630,9 +4766,6 @@ static int msm_pcie_enable(struct msm_pcie_dev_t *dev)
			msleep(dev->switch_latency);
	}

	msm_pcie_config_sid(dev);
	msm_pcie_config_controller(dev);

	/* check endpoint configuration space is accessible */
	while (time_before(jiffies, ep_up_timeout)) {
		if (readl_relaxed(dev->conf) != PCIE_LINK_DOWN)
@@ -4655,10 +4788,8 @@ static int msm_pcie_enable(struct msm_pcie_dev_t *dev)
		goto link_fail;
	}

	if (dev->enumerated) {
	if (dev->enumerated)
		msm_msi_config(dev_get_msi_domain(&dev->dev->dev));
		msm_pcie_config_link_pm(dev, true);
	}

	/* bring eps out of reset */
	if (dev->i2c_ctrl.client && dev->i2c_ctrl.client_i2c_reset)
@@ -4981,8 +5112,6 @@ int msm_pcie_enumerate(u32 rc_idx)

	pci_walk_bus(dev->dev->bus, msm_pcie_config_device_table, dev);

	msm_pcie_check_l1ss_support_all(dev);
	msm_pcie_config_link_pm(dev, true);
out:
	mutex_unlock(&dev->enumerate_lock);

@@ -5546,64 +5675,6 @@ static int msm_pcie_check_l1ss_support(struct pci_dev *pdev, void *dev)
	return 0;
}

static int msm_pcie_config_common_clock_enable(struct pci_dev *pdev,
							void *dev)
{
	struct msm_pcie_dev_t *pcie_dev = (struct msm_pcie_dev_t *)dev;

	PCIE_DBG(pcie_dev, "PCIe: RC%d: PCI device %02x:%02x.%01x\n",
		pcie_dev->rc_idx, pdev->bus->number, PCI_SLOT(pdev->devfn),
		PCI_FUNC(pdev->devfn));

	msm_pcie_config_clear_set_dword(pdev, pdev->pcie_cap + PCI_EXP_LNKCTL,
					0, PCI_EXP_LNKCTL_CCC);

	return 0;
}

static void msm_pcie_config_common_clock_enable_all(struct msm_pcie_dev_t *dev)
{
	if (dev->common_clk_en)
		pci_walk_bus(dev->dev->bus,
			msm_pcie_config_common_clock_enable, dev);
}

static int msm_pcie_config_clock_power_management_enable(struct pci_dev *pdev,
							void *dev)
{
	struct msm_pcie_dev_t *pcie_dev = (struct msm_pcie_dev_t *)dev;
	u32 val;

	/* enable only for upstream ports */
	if (pci_is_root_bus(pdev->bus))
		return 0;

	PCIE_DBG(pcie_dev, "PCIe: RC%d: PCI device %02x:%02x.%01x\n",
		pcie_dev->rc_idx, pdev->bus->number, PCI_SLOT(pdev->devfn),
		PCI_FUNC(pdev->devfn));

	pci_read_config_dword(pdev, pdev->pcie_cap + PCI_EXP_LNKCAP, &val);
	if (val & PCI_EXP_LNKCAP_CLKPM)
		msm_pcie_config_clear_set_dword(pdev,
			pdev->pcie_cap + PCI_EXP_LNKCTL, 0,
			PCI_EXP_LNKCTL_CLKREQ_EN);
	else
		PCIE_DBG(pcie_dev,
			"PCIe: RC%d: PCI device %02x:%02x.%01x does not support clock power management\n",
			pcie_dev->rc_idx, pdev->bus->number,
			PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));

	return 0;
}

static void msm_pcie_config_clock_power_management_enable_all(
						struct msm_pcie_dev_t *dev)
{
	if (dev->clk_power_manage_en)
		pci_walk_bus(dev->dev->bus,
			msm_pcie_config_clock_power_management_enable, dev);
}

static void msm_pcie_config_l0s(struct msm_pcie_dev_t *dev,
				struct pci_dev *pdev, bool enable)
{
@@ -5854,23 +5925,6 @@ static void msm_pcie_config_l1ss_enable_all(struct msm_pcie_dev_t *dev)
	}
}

static void msm_pcie_config_link_pm(struct msm_pcie_dev_t *dev, bool enable)
{
	struct pci_bus *bus = dev->dev->bus;

	if (enable) {
		msm_pcie_config_common_clock_enable_all(dev);
		msm_pcie_config_clock_power_management_enable_all(dev);
		msm_pcie_config_l1ss_enable_all(dev);
		msm_pcie_config_l1_enable_all(dev);
		msm_pcie_config_l0s_enable_all(dev);
	} else {
		msm_pcie_config_l0s_disable_all(dev, bus);
		msm_pcie_config_l1_disable_all(dev, bus);
		msm_pcie_config_l1ss_disable_all(dev, bus);
	}
}

static void msm_pcie_check_l1ss_support_all(struct msm_pcie_dev_t *dev)
{
	pci_walk_bus(dev->dev->bus, msm_pcie_check_l1ss_support, dev);
@@ -6055,6 +6109,14 @@ static int msm_pcie_probe(struct platform_device *pdev)
		pcie_dev->rc_idx, pcie_dev->l1_2_th_scale,
		pcie_dev->l1_2_th_value);

	of_property_read_u32(of_node, "qcom,tpwr-on-scale",
			     &pcie_dev->t_pwr_on_scale);
	of_property_read_u32(of_node, "qcom,tpwr-on-value",
			     &pcie_dev->t_pwr_on_val);
	PCIE_DBG(pcie_dev, "PCIe: RC%d: t_power on scale: %u value: %u.\n",
		 pcie_dev->rc_idx, pcie_dev->t_pwr_on_scale,
		 pcie_dev->t_pwr_on_val);

	pcie_dev->common_clk_en = of_property_read_bool(of_node,
				"qcom,common-clk-en");
	PCIE_DBG(pcie_dev, "Common clock is %s enabled.\n",