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

Commit e361431b authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: pcie: provide APIs to prevent and allow PCIe ASPM L1"

parents 9b38611e 4074521a
Loading
Loading
Loading
Loading
+103 −20
Original line number Diff line number Diff line
@@ -725,6 +725,8 @@ struct msm_pcie_dev_t {
	struct mutex recovery_lock;
	spinlock_t wakeup_lock;
	spinlock_t irq_lock;
	struct mutex aspm_lock;
	int prevent_l1;
	ulong linkdown_counter;
	ulong link_turned_on_counter;
	ulong link_turned_off_counter;
@@ -1392,6 +1394,8 @@ static void msm_pcie_show_status(struct msm_pcie_dev_t *dev)
		dev->wake_counter);
	PCIE_DBG_FS(dev, "link_check_max_count: %u\n",
		dev->link_check_max_count);
	PCIE_DBG_FS(dev, "prevent_l1: %d\n",
		dev->prevent_l1);
	PCIE_DBG_FS(dev, "target_link_speed: 0x%x\n",
		dev->target_link_speed);
	PCIE_DBG_FS(dev, "link_turned_on_counter: %lu\n",
@@ -6051,20 +6055,6 @@ static int msm_pcie_link_retrain(struct msm_pcie_dev_t *pcie_dev,
	u32 cnt_max = 1000; /* 100ms timeout */
	u32 link_status_lbms_mask = PCI_EXP_LNKSTA_LBMS << PCI_EXP_LNKCTL;

	cnt = 0;
	/* confirm link is in L0 */
	while (((readl_relaxed(pcie_dev->parf + PCIE20_PARF_LTSSM) &
		MSM_PCIE_LTSSM_MASK)) != MSM_PCIE_LTSSM_L0) {
		if (unlikely(cnt++ >= cnt_max)) {
			PCIE_ERR(pcie_dev,
				"PCIe: RC%d: failed to transition to L0\n",
				pcie_dev->rc_idx);
			return -EIO;
		}

		usleep_range(100, 105);
	}

	/* link retrain */
	msm_pcie_config_clear_set_dword(pci_dev,
					pci_dev->pcie_cap + PCI_EXP_LNKCTL,
@@ -6113,6 +6103,99 @@ static int msm_pcie_set_link_width(struct msm_pcie_dev_t *pcie_dev,
	return 0;
}

void msm_pcie_allow_l1(struct pci_dev *pci_dev)
{
	struct pci_dev *root_pci_dev;
	struct msm_pcie_dev_t *pcie_dev;

	root_pci_dev = pci_find_pcie_root_port(pci_dev);
	if (!root_pci_dev)
		return;

	pcie_dev = PCIE_BUS_PRIV_DATA(root_pci_dev->bus);

	mutex_lock(&pcie_dev->aspm_lock);
	if (unlikely(--pcie_dev->prevent_l1 < 0))
		PCIE_ERR(pcie_dev,
			"PCIe: RC%d: %02x:%02x.%01x: unbalanced prevent_l1: %d < 0\n",
			pcie_dev->rc_idx, pci_dev->bus->number,
			PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn),
			pcie_dev->prevent_l1);

	if (pcie_dev->prevent_l1) {
		mutex_unlock(&pcie_dev->aspm_lock);
		return;
	}

	msm_pcie_write_mask(pcie_dev->parf + PCIE20_PARF_PM_CTRL, BIT(5), 0);
	/* enable L1 */
	msm_pcie_write_mask(pcie_dev->dm_core +
				(root_pci_dev->pcie_cap + PCI_EXP_LNKCTL),
				0, PCI_EXP_LNKCTL_ASPM_L1);

	PCIE_DBG2(pcie_dev, "PCIe: RC%d: %02x:%02x.%01x: exit\n",
		pcie_dev->rc_idx, pci_dev->bus->number,
		PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn));
	mutex_unlock(&pcie_dev->aspm_lock);
}
EXPORT_SYMBOL(msm_pcie_allow_l1);

int msm_pcie_prevent_l1(struct pci_dev *pci_dev)
{
	struct pci_dev *root_pci_dev;
	struct msm_pcie_dev_t *pcie_dev;
	u32 cnt = 0;
	u32 cnt_max = 1000; /* 100ms timeout */
	int ret = 0;

	root_pci_dev = pci_find_pcie_root_port(pci_dev);
	if (!root_pci_dev)
		return -ENODEV;

	pcie_dev = PCIE_BUS_PRIV_DATA(root_pci_dev->bus);

	/* disable L1 */
	mutex_lock(&pcie_dev->aspm_lock);
	if (pcie_dev->prevent_l1++) {
		mutex_unlock(&pcie_dev->aspm_lock);
		return 0;
	}

	msm_pcie_write_mask(pcie_dev->dm_core +
				(root_pci_dev->pcie_cap + PCI_EXP_LNKCTL),
				PCI_EXP_LNKCTL_ASPM_L1, 0);
	msm_pcie_write_mask(pcie_dev->parf + PCIE20_PARF_PM_CTRL, 0, BIT(5));

	/* confirm link is in L0 */
	while (((readl_relaxed(pcie_dev->parf + PCIE20_PARF_LTSSM) &
		MSM_PCIE_LTSSM_MASK)) != MSM_PCIE_LTSSM_L0) {
		if (unlikely(cnt++ >= cnt_max)) {
			PCIE_ERR(pcie_dev,
				"PCIe: RC%d: %02x:%02x.%01x: failed to transition to L0\n",
				pcie_dev->rc_idx, pci_dev->bus->number,
				PCI_SLOT(pci_dev->devfn),
				PCI_FUNC(pci_dev->devfn));
			ret = -EIO;
			goto err;
		}

		usleep_range(100, 105);
	}

	PCIE_DBG2(pcie_dev, "PCIe: RC%d: %02x:%02x.%01x: exit\n",
		pcie_dev->rc_idx, pci_dev->bus->number,
		PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn));
	mutex_unlock(&pcie_dev->aspm_lock);

	return 0;
err:
	mutex_unlock(&pcie_dev->aspm_lock);
	msm_pcie_allow_l1(pci_dev);

	return ret;
}
EXPORT_SYMBOL(msm_pcie_prevent_l1);

int msm_pcie_set_link_bandwidth(struct pci_dev *pci_dev, u16 target_link_speed,
				u16 target_link_width)
{
@@ -6162,9 +6245,10 @@ int msm_pcie_set_link_bandwidth(struct pci_dev *pci_dev, u16 target_link_speed,
						PCI_EXP_LNKSTA_CLS,
						target_link_speed);

	/* disable link L1. Need to be in L0 for gen switch */
	msm_pcie_config_l1(pcie_dev, root_pci_dev, false);
	msm_pcie_write_mask(pcie_dev->parf + PCIE20_PARF_PM_CTRL,  0, BIT(5));
	/* need to be in L0 for gen switch */
	ret = msm_pcie_prevent_l1(root_pci_dev);
	if (ret)
		return ret;

	if (target_link_speed > current_link_speed)
		msm_pcie_scale_link_bandwidth(pcie_dev, target_link_speed);
@@ -6187,9 +6271,7 @@ int msm_pcie_set_link_bandwidth(struct pci_dev *pci_dev, u16 target_link_speed,
	if (target_link_speed < current_link_speed)
		msm_pcie_scale_link_bandwidth(pcie_dev, target_link_speed);
out:
	/* re-enable link L1 */
	msm_pcie_write_mask(pcie_dev->parf + PCIE20_PARF_PM_CTRL, BIT(5), 0);
	msm_pcie_config_l1(pcie_dev, root_pci_dev, true);
	msm_pcie_allow_l1(root_pci_dev);

	return ret;
}
@@ -6500,6 +6582,7 @@ static int __init pcie_init(void)
		mutex_init(&msm_pcie_dev[i].setup_lock);
		mutex_init(&msm_pcie_dev[i].clk_lock);
		mutex_init(&msm_pcie_dev[i].recovery_lock);
		mutex_init(&msm_pcie_dev[i].aspm_lock);
		spin_lock_init(&msm_pcie_dev[i].wakeup_lock);
		spin_lock_init(&msm_pcie_dev[i].irq_lock);
		msm_pcie_dev[i].drv_ready = false;
+29 −0
Original line number Diff line number Diff line
@@ -65,6 +65,26 @@ static inline int msm_msi_init(struct device *dev)

#ifdef CONFIG_PCI_MSM

/**
 * msm_pcie_allow_l1 - allow PCIe link to re-enter L1
 * @pci_dev:		client's pci device structure
 *
 * This function gives PCIe clients the control to allow the link to re-enter
 * L1. Should only be used after msm_pcie_prevent_l1 has been called.
 */
void msm_pcie_allow_l1(struct pci_dev *pci_dev);

/**
 * msm_pcie_prevent_l1 - keeps PCIe link out of L1
 * @pci_dev:		client's pci device structure
 *
 * This function gives PCIe clients the control to exit and prevent the link
 * from entering L1.
 *
 * Return 0 on success, negative value on error
 */
int msm_pcie_prevent_l1(struct pci_dev *pci_dev);

/**
 * msm_pcie_set_link_bandwidth - updates the number of lanes and speed of PCIe
 * link.
@@ -202,6 +222,15 @@ static inline int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr,
	return -ENODEV;
}

static inline void msm_pcie_allow_l1(struct pci_dev *pci_dev)
{
}

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

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