aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorqctecmdr <qctecmdr@localhost>2019-12-14 06:17:18 -0800
committerGerrit - the friendly Code Review server <code-review@localhost>2019-12-14 06:17:17 -0800
commitab477afb1781e35813d9a21e02f7c826c7b4eb88 (patch)
tree35aa87fadf4b6c80d7be3269708d571f9690da33
parentf8df7a03436986424b140f917b482dff9ca5b9d8 (diff)
parent33322a80dbe9e8be604ab99d883f394e529eaf30 (diff)
Merge "msm: pcie: provide APIs to prevent and allow PCIe ASPM L1"LE.UM.3.2.3-17500-SA2150p
-rw-r--r--drivers/pci/host/pci-msm.c123
-rw-r--r--include/linux/msm_pcie.h29
2 files changed, 132 insertions, 20 deletions
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index f31a23d40e7c..88c3c3df7024 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -714,6 +714,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;
@@ -1370,6 +1372,8 @@ static void msm_pcie_show_status(struct msm_pcie_dev_t *dev)
dev->linkdown_counter);
PCIE_DBG_FS(dev, "wake_counter: %lu\n",
dev->wake_counter);
+ 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",
@@ -6151,20 +6155,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,
@@ -6213,6 +6203,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)
{
@@ -6262,9 +6345,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);
@@ -6287,9 +6371,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;
}
@@ -6553,6 +6635,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;
diff --git a/include/linux/msm_pcie.h b/include/linux/msm_pcie.h
index 58d4fb22733d..a5020e767d13 100644
--- a/include/linux/msm_pcie.h
+++ b/include/linux/msm_pcie.h
@@ -82,6 +82,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_request_not_enter_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.
* @pci_dev: client's pci device structure
@@ -218,6 +238,15 @@ static inline int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr,
return -ENODEV;
}
+static inline void msm_pcie_request_allow_l1(struct pci_dev *pci_dev)
+{
+}
+
+static inline int msm_pcie_request_not_enter_l1(struct pci_dev *pci_dev)
+{
+ return -ENODEV;
+}
+
static inline int msm_pcie_l1ss_timeout_disable(struct pci_dev *pci_dev)
{
return -ENODEV;