Merge "cnss2: Expose APIs to prevent and allow PCIe L1 state"

This commit is contained in:
qctecmdr 2020-02-27 00:18:52 -08:00 • committed by Gerrit - the friendly Code Review server
commit eeea05d3fe
4 changed files with 151 additions and 21 deletions

View file

@ -808,6 +808,22 @@ out:
return ret;
}
int cnss_pci_prevent_l1(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
return msm_pcie_prevent_l1(pci_dev);
}
EXPORT_SYMBOL(cnss_pci_prevent_l1);
void cnss_pci_allow_l1(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
msm_pcie_allow_l1(pci_dev);
}
EXPORT_SYMBOL(cnss_pci_allow_l1);
int cnss_pci_link_down(struct device *dev)
{
unsigned long flags;

View file

@ -718,6 +718,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;
@ -1321,6 +1323,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",
@ -5997,20 +6001,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,
@ -6059,6 +6049,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)
{
@ -6108,9 +6191,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);
@ -6133,9 +6217,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;
}
@ -6444,6 +6526,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;

View file

@ -58,6 +58,26 @@ int msm_msi_init(struct device *dev);
#if IS_ENABLED(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.
@ -195,6 +215,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;

View file

@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */
/* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. */
#ifndef _NET_CNSS2_H
#define _NET_CNSS2_H
@ -158,6 +158,8 @@ enum cnss_recovery_reason {
extern int cnss_wlan_register_driver(struct cnss_wlan_driver *driver);
extern void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver);
extern void cnss_device_crashed(struct device *dev);
extern int cnss_pci_prevent_l1(struct device *dev);
extern void cnss_pci_allow_l1(struct device *dev);
extern int cnss_pci_link_down(struct device *dev);
extern int cnss_pci_is_device_down(struct device *dev);
extern void cnss_schedule_recovery(struct device *dev,