From 411dc32d8810e0a204c799ce5c97cb56990de1cb Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Mon, 15 Aug 2016 14:06:02 +0800 Subject: PCI: altera: Poll for link training status after retraining the link Poll for link training status is cleared before poll for link up status. This can help to get the reliable link up status, especially when PCIe is in Gen 3 speed. Signed-off-by: Ley Foon Tan Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pcie-altera.c | 45 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) (limited to 'drivers/pci/host/pcie-altera.c') diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c index 2b7837650db8..58eef9963752 100644 --- a/drivers/pci/host/pcie-altera.c +++ b/drivers/pci/host/pcie-altera.c @@ -61,7 +61,8 @@ #define TLP_LOOP 500 #define RP_DEVFN 0 -#define LINK_UP_TIMEOUT 5000 +#define LINK_UP_TIMEOUT HZ +#define LINK_RETRAIN_TIMEOUT HZ #define INTX_NUM 4 @@ -99,11 +100,44 @@ static bool altera_pcie_link_is_up(struct altera_pcie *pcie) return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0); } +static void altera_wait_link_retrain(struct pci_dev *dev) +{ + u16 reg16; + unsigned long start_jiffies; + struct altera_pcie *pcie = dev->bus->sysdata; + + /* Wait for link training end. */ + start_jiffies = jiffies; + for (;;) { + pcie_capability_read_word(dev, PCI_EXP_LNKSTA, ®16); + if (!(reg16 & PCI_EXP_LNKSTA_LT)) + break; + + if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT)) { + dev_err(&pcie->pdev->dev, "link retrain timeout\n"); + break; + } + udelay(100); + } + + /* Wait for link is up */ + start_jiffies = jiffies; + for (;;) { + if (altera_pcie_link_is_up(pcie)) + break; + + if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) { + dev_err(&pcie->pdev->dev, "link up timeout\n"); + break; + } + udelay(100); + } +} + static void altera_pcie_retrain(struct pci_dev *dev) { u16 linkcap, linkstat; struct altera_pcie *pcie = dev->bus->sysdata; - int timeout = 0; if (!altera_pcie_link_is_up(pcie)) return; @@ -121,12 +155,7 @@ static void altera_pcie_retrain(struct pci_dev *dev) if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) { pcie_capability_set_word(dev, PCI_EXP_LNKCTL, PCI_EXP_LNKCTL_RL); - while (!altera_pcie_link_is_up(pcie)) { - timeout++; - if (timeout > LINK_UP_TIMEOUT) - break; - udelay(5); - } + altera_wait_link_retrain(dev); } } DECLARE_PCI_FIXUP_EARLY(0x1172, PCI_ANY_ID, altera_pcie_retrain); -- cgit From 31fc0ad47e2e0b8417616aa0f1ddcc67edf1e109 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 26 Aug 2016 09:47:24 +0800 Subject: PCI: altera: Rework config accessors for use without a struct pci_bus Rework configs accessors so a future patch can use them in _probe() with struct altera_pcie instead of struct pci_bus. Signed-off-by: Ley Foon Tan Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pcie-altera.c | 64 +++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 23 deletions(-) (limited to 'drivers/pci/host/pcie-altera.c') diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c index 58eef9963752..34e6258992c1 100644 --- a/drivers/pci/host/pcie-altera.c +++ b/drivers/pci/host/pcie-altera.c @@ -330,22 +330,14 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn, return PCIBIOS_SUCCESSFUL; } -static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *value) +static int _altera_pcie_cfg_read(struct altera_pcie *pcie, u8 busno, + unsigned int devfn, int where, int size, + u32 *value) { - struct altera_pcie *pcie = bus->sysdata; int ret; u32 data; u8 byte_en; - if (altera_pcie_hide_rc_bar(bus, devfn, where)) - return PCIBIOS_BAD_REGISTER_NUMBER; - - if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) { - *value = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - switch (size) { case 1: byte_en = 1 << (where & 3); @@ -358,7 +350,7 @@ static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn, break; } - ret = tlp_cfg_dword_read(pcie, bus->number, devfn, + ret = tlp_cfg_dword_read(pcie, busno, devfn, (where & ~DWORD_MASK), byte_en, &data); if (ret != PCIBIOS_SUCCESSFUL) return ret; @@ -378,20 +370,14 @@ static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn, return PCIBIOS_SUCCESSFUL; } -static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 value) +static int _altera_pcie_cfg_write(struct altera_pcie *pcie, u8 busno, + unsigned int devfn, int where, int size, + u32 value) { - struct altera_pcie *pcie = bus->sysdata; u32 data32; u32 shift = 8 * (where & 3); u8 byte_en; - if (altera_pcie_hide_rc_bar(bus, devfn, where)) - return PCIBIOS_BAD_REGISTER_NUMBER; - - if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) - return PCIBIOS_DEVICE_NOT_FOUND; - switch (size) { case 1: data32 = (value & 0xff) << shift; @@ -407,8 +393,40 @@ static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn, break; } - return tlp_cfg_dword_write(pcie, bus->number, devfn, - (where & ~DWORD_MASK), byte_en, data32); + return tlp_cfg_dword_write(pcie, busno, devfn, (where & ~DWORD_MASK), + byte_en, data32); +} + +static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *value) +{ + struct altera_pcie *pcie = bus->sysdata; + + if (altera_pcie_hide_rc_bar(bus, devfn, where)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) { + *value = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + return _altera_pcie_cfg_read(pcie, bus->number, devfn, where, size, + value); +} + +static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + struct altera_pcie *pcie = bus->sysdata; + + if (altera_pcie_hide_rc_bar(bus, devfn, where)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) + return PCIBIOS_DEVICE_NOT_FOUND; + + return _altera_pcie_cfg_write(pcie, bus->number, devfn, where, size, + value); } static struct pci_ops altera_pcie_ops = { -- cgit From ce4f1c7ad490aa7129bde5632d6e53943f8a866c Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 26 Aug 2016 09:47:25 +0800 Subject: PCI: altera: Move retrain from fixup to altera_pcie_host_init() Previously we used a PCI early fixup to initiate a link retrain on Altera devices. But Altera PCIe IP can be configured as either a Root Port or an Endpoint, and they might have same vendor ID, so the fixup would be run for both. We only want to initiate a link retrain for Altera Root Port devices, not for Endpoints, so move the link retrain functionality from the fixup to altera_pcie_host_init(). [bhelgaas: changelog] Signed-off-by: Ley Foon Tan Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pcie-altera.c | 151 +++++++++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 60 deletions(-) (limited to 'drivers/pci/host/pcie-altera.c') diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c index 34e6258992c1..4ca50a28af82 100644 --- a/drivers/pci/host/pcie-altera.c +++ b/drivers/pci/host/pcie-altera.c @@ -43,6 +43,7 @@ #define RP_LTSSM_MASK 0x1f #define LTSSM_L0 0xf +#define PCIE_CAP_OFFSET 0x80 /* TLP configuration type 0 and 1 */ #define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */ #define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */ @@ -100,66 +101,6 @@ static bool altera_pcie_link_is_up(struct altera_pcie *pcie) return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0); } -static void altera_wait_link_retrain(struct pci_dev *dev) -{ - u16 reg16; - unsigned long start_jiffies; - struct altera_pcie *pcie = dev->bus->sysdata; - - /* Wait for link training end. */ - start_jiffies = jiffies; - for (;;) { - pcie_capability_read_word(dev, PCI_EXP_LNKSTA, ®16); - if (!(reg16 & PCI_EXP_LNKSTA_LT)) - break; - - if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT)) { - dev_err(&pcie->pdev->dev, "link retrain timeout\n"); - break; - } - udelay(100); - } - - /* Wait for link is up */ - start_jiffies = jiffies; - for (;;) { - if (altera_pcie_link_is_up(pcie)) - break; - - if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) { - dev_err(&pcie->pdev->dev, "link up timeout\n"); - break; - } - udelay(100); - } -} - -static void altera_pcie_retrain(struct pci_dev *dev) -{ - u16 linkcap, linkstat; - struct altera_pcie *pcie = dev->bus->sysdata; - - if (!altera_pcie_link_is_up(pcie)) - return; - - /* - * Set the retrain bit if the PCIe rootport support > 2.5GB/s, but - * current speed is 2.5 GB/s. - */ - pcie_capability_read_word(dev, PCI_EXP_LNKCAP, &linkcap); - - if ((linkcap & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB) - return; - - pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &linkstat); - if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) { - pcie_capability_set_word(dev, PCI_EXP_LNKCTL, - PCI_EXP_LNKCTL_RL); - altera_wait_link_retrain(dev); - } -} -DECLARE_PCI_FIXUP_EARLY(0x1172, PCI_ANY_ID, altera_pcie_retrain); - /* * Altera PCIe port uses BAR0 of RC's configuration space as the translation * from PCI bus to native BUS. Entire DDR region is mapped into PCIe space @@ -434,6 +375,90 @@ static struct pci_ops altera_pcie_ops = { .write = altera_pcie_cfg_write, }; +static int altera_read_cap_word(struct altera_pcie *pcie, u8 busno, + unsigned int devfn, int offset, u16 *value) +{ + u32 data; + int ret; + + ret = _altera_pcie_cfg_read(pcie, busno, devfn, + PCIE_CAP_OFFSET + offset, sizeof(*value), + &data); + *value = data; + return ret; +} + +static int altera_write_cap_word(struct altera_pcie *pcie, u8 busno, + unsigned int devfn, int offset, u16 value) +{ + return _altera_pcie_cfg_write(pcie, busno, devfn, + PCIE_CAP_OFFSET + offset, sizeof(value), + value); +} + +static void altera_wait_link_retrain(struct altera_pcie *pcie) +{ + u16 reg16; + unsigned long start_jiffies; + + /* Wait for link training end. */ + start_jiffies = jiffies; + for (;;) { + altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, + PCI_EXP_LNKSTA, ®16); + if (!(reg16 & PCI_EXP_LNKSTA_LT)) + break; + + if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT)) { + dev_err(&pcie->pdev->dev, "link retrain timeout\n"); + break; + } + udelay(100); + } + + /* Wait for link is up */ + start_jiffies = jiffies; + for (;;) { + if (altera_pcie_link_is_up(pcie)) + break; + + if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) { + dev_err(&pcie->pdev->dev, "link up timeout\n"); + break; + } + udelay(100); + } +} + +static void altera_pcie_retrain(struct altera_pcie *pcie) +{ + u16 linkcap, linkstat, linkctl; + + if (!altera_pcie_link_is_up(pcie)) + return; + + /* + * Set the retrain bit if the PCIe rootport support > 2.5GB/s, but + * current speed is 2.5 GB/s. + */ + altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, PCI_EXP_LNKCAP, + &linkcap); + if ((linkcap & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB) + return; + + altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, PCI_EXP_LNKSTA, + &linkstat); + if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) { + altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, + PCI_EXP_LNKCTL, &linkctl); + linkctl |= PCI_EXP_LNKCTL_RL; + altera_write_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, + PCI_EXP_LNKCTL, linkctl); + + altera_wait_link_retrain(pcie); + } +} + static int altera_pcie_intx_map(struct irq_domain *domain, unsigned int irq, irq_hw_number_t hwirq) { @@ -555,6 +580,11 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie) return 0; } +static void altera_pcie_host_init(struct altera_pcie *pcie) +{ + altera_pcie_retrain(pcie); +} + static int altera_pcie_probe(struct platform_device *pdev) { struct altera_pcie *pcie; @@ -592,6 +622,7 @@ static int altera_pcie_probe(struct platform_device *pdev) cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS); /* enable all interrupts */ cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE); + altera_pcie_host_init(pcie); bus = pci_scan_root_bus(&pdev->dev, pcie->root_bus_nr, &altera_pcie_ops, pcie, &pcie->resources); -- cgit