diff --git a/drivers/pci/controller/pci-msm.c b/drivers/pci/controller/pci-msm.c index d9c68d73fb3b..d34b0e8dcd45 100644 --- a/drivers/pci/controller/pci-msm.c +++ b/drivers/pci/controller/pci-msm.c @@ -173,6 +173,7 @@ #define REFCLK_STABILIZATION_DELAY_US_MAX (1005) #define LINK_UP_TIMEOUT_US_MIN (5000) #define LINK_UP_TIMEOUT_US_MAX (5100) +#define LINK_CHECK_COUNT_MAX (20) #define LINK_UP_CHECK_MAX_COUNT (20) #define EP_UP_TIMEOUT_US_MIN (1000) #define EP_UP_TIMEOUT_US_MAX (1005) @@ -6608,6 +6609,88 @@ static int msm_pcie_set_link_width(struct msm_pcie_dev_t *pcie_dev, return 0; } +int msm_pcie_dsp_link_control(struct pci_dev *pci_dev, + bool link_enable) +{ + int ret = 0; + struct pci_dev *dsp_dev = NULL; + u16 link_control = 0; + u16 link_status = 0; + u32 link_capability = 0; + int link_check_count = 0; + bool link_trained = false; + struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(pci_dev->bus); + + if (!pcie_dev->power_on) + return 0; + + dsp_dev = pci_dev->bus->self; + if (pci_pcie_type(dsp_dev) != PCI_EXP_TYPE_DOWNSTREAM) { + PCIE_DBG(pcie_dev, + "PCIe: RC%d: no DSP<->EP link under this RC\n", + pcie_dev->rc_idx); + return 0; + } + + pci_read_config_dword(dsp_dev, dsp_dev->pcie_cap + PCI_EXP_LNKCAP, + &link_capability); + pci_read_config_word(dsp_dev, dsp_dev->pcie_cap + PCI_EXP_LNKCTL, + &link_control); + + if (link_enable) { + link_control &= ~PCI_EXP_LNKCTL_LD; + pci_write_config_word(dsp_dev, + dsp_dev->pcie_cap + PCI_EXP_LNKCTL, + link_control); + PCIE_DBG(pcie_dev, + "PCIe: RC%d: DSP<->EP Link is enabled\n", + pcie_dev->rc_idx); + + /* Wait for up to 100ms for the link to come up */ + do { + usleep_range(LINK_UP_TIMEOUT_US_MIN, + LINK_UP_TIMEOUT_US_MAX); + pci_read_config_word(dsp_dev, + dsp_dev->pcie_cap + PCI_EXP_LNKSTA, + &link_status); + if (link_capability & PCI_EXP_LNKCAP_DLLLARC) + link_trained = (!(link_status & + PCI_EXP_LNKSTA_LT)) && + (link_status & + PCI_EXP_LNKSTA_DLLLA); + else + link_trained = !(link_status & + PCI_EXP_LNKSTA_LT); + + if (link_trained) + break; + } while (link_check_count++ < LINK_CHECK_COUNT_MAX); + + if (link_trained) { + PCIE_DBG(pcie_dev, + "PCIe: RC%d: DSP<->EP link status: 0x%04x\n", + pcie_dev->rc_idx, link_status); + PCIE_DBG(pcie_dev, + "PCIe: RC%d: DSP<->EP Link is up after %d checkings\n", + pcie_dev->rc_idx, link_check_count); + } else { + PCIE_DBG(pcie_dev, "DSP<->EP link initialization failed\n"); + ret = MSM_PCIE_ERROR; + } + } else { + link_control |= PCI_EXP_LNKCTL_LD; + pci_write_config_word(dsp_dev, + dsp_dev->pcie_cap + PCI_EXP_LNKCTL, + link_control); + PCIE_DBG(pcie_dev, + "PCIe: RC%d: DSP<->EP Link is disabled\n", + pcie_dev->rc_idx); + } + + return ret; +} +EXPORT_SYMBOL(msm_pcie_dsp_link_control); + void msm_pcie_allow_l1(struct pci_dev *pci_dev) { struct pci_dev *root_pci_dev; diff --git a/include/linux/msm_pcie.h b/include/linux/msm_pcie.h index d728aeaf6999..e2f701ae118c 100644 --- a/include/linux/msm_pcie.h +++ b/include/linux/msm_pcie.h @@ -212,6 +212,17 @@ int msm_pcie_debug_info(struct pci_dev *dev, u32 option, u32 base, */ int msm_pcie_reg_dump(struct pci_dev *pci_dev, u8 *buff, u32 len); +/* + * msm_pcie_dsp_link_control - enable/disable DSP link + * @pci_dev: pci device structure, endpoint of this DSP + * @link_enable true to enable, false to disable + * + * This function enable(include training)/disable link between PCIe + * switch DSP and endpoint attached. + * Return: 0 on success, negative value on error + */ +int msm_pcie_dsp_link_control(struct pci_dev *pci_dev, + bool link_enable); #else /* !CONFIG_PCI_MSM */ static inline int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user, void *data, u32 options) @@ -269,6 +280,12 @@ static inline int msm_pcie_reg_dump(struct pci_dev *pci_dev, u8 *buff, u32 len) { return -ENODEV; } + +static inline int msm_pcie_dsp_link_control(struct pci_dev *pci_dev, + bool link_enable) +{ + return -ENODEV; +} #endif /* CONFIG_PCI_MSM */ #endif /* __MSM_PCIE_H */