summaryrefslogtreecommitdiffstats
path: root/drivers/pci/controller/dwc/pcie-fu740.c
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2022-03-22 17:16:24 -0500
committerBjorn Helgaas <bhelgaas@google.com>2022-03-22 17:16:24 -0500
commitd93fefad2060402028fcd2537ea5e784aca003d6 (patch)
tree13895c0fb3355c05ae19e2d3fcfe5903149e2223 /drivers/pci/controller/dwc/pcie-fu740.c
parenta69e89ba6c810295b4ce795ce38088788857822f (diff)
parenta382c757ec5ef83137a86125f43a4c43dc2ab50b (diff)
downloadlinux-stable-d93fefad2060402028fcd2537ea5e784aca003d6.tar.gz
linux-stable-d93fefad2060402028fcd2537ea5e784aca003d6.tar.bz2
linux-stable-d93fefad2060402028fcd2537ea5e784aca003d6.zip
Merge branch 'pci/host/fu740'
- Drop redundant '-gpios' from DT GPIO lookup (Ben Dooks) - Force 2.5GT/s for initial device probe to workaround enumeration issue on SiFive Unmatched board (Ben Dooks) * pci/host/fu740: PCI: fu740: Force 2.5GT/s for initial device probe PCI: fu740: Drop redundant '-gpios' from DT GPIO lookup
Diffstat (limited to 'drivers/pci/controller/dwc/pcie-fu740.c')
-rw-r--r--drivers/pci/controller/dwc/pcie-fu740.c55
1 files changed, 52 insertions, 3 deletions
diff --git a/drivers/pci/controller/dwc/pcie-fu740.c b/drivers/pci/controller/dwc/pcie-fu740.c
index 43b7b8e18354..02cc70d8cc06 100644
--- a/drivers/pci/controller/dwc/pcie-fu740.c
+++ b/drivers/pci/controller/dwc/pcie-fu740.c
@@ -181,10 +181,59 @@ static int fu740_pcie_start_link(struct dw_pcie *pci)
{
struct device *dev = pci->dev;
struct fu740_pcie *afp = dev_get_drvdata(dev);
+ u8 cap_exp = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ int ret;
+ u32 orig, tmp;
+
+ /*
+ * Force 2.5GT/s when starting the link, due to some devices not
+ * probing at higher speeds. This happens with the PCIe switch
+ * on the Unmatched board when U-Boot has not initialised the PCIe.
+ * The fix in U-Boot is to force 2.5GT/s, which then gets cleared
+ * by the soft reset done by this driver.
+ */
+ dev_dbg(dev, "cap_exp at %x\n", cap_exp);
+ dw_pcie_dbi_ro_wr_en(pci);
+
+ tmp = dw_pcie_readl_dbi(pci, cap_exp + PCI_EXP_LNKCAP);
+ orig = tmp & PCI_EXP_LNKCAP_SLS;
+ tmp &= ~PCI_EXP_LNKCAP_SLS;
+ tmp |= PCI_EXP_LNKCAP_SLS_2_5GB;
+ dw_pcie_writel_dbi(pci, cap_exp + PCI_EXP_LNKCAP, tmp);
/* Enable LTSSM */
writel_relaxed(0x1, afp->mgmt_base + PCIEX8MGMT_APP_LTSSM_ENABLE);
- return 0;
+
+ ret = dw_pcie_wait_for_link(pci);
+ if (ret) {
+ dev_err(dev, "error: link did not start\n");
+ goto err;
+ }
+
+ tmp = dw_pcie_readl_dbi(pci, cap_exp + PCI_EXP_LNKCAP);
+ if ((tmp & PCI_EXP_LNKCAP_SLS) != orig) {
+ dev_dbg(dev, "changing speed back to original\n");
+
+ tmp &= ~PCI_EXP_LNKCAP_SLS;
+ tmp |= orig;
+ dw_pcie_writel_dbi(pci, cap_exp + PCI_EXP_LNKCAP, tmp);
+
+ tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+ tmp |= PORT_LOGIC_SPEED_CHANGE;
+ dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
+
+ ret = dw_pcie_wait_for_link(pci);
+ if (ret) {
+ dev_err(dev, "error: link did not start at new speed\n");
+ goto err;
+ }
+ }
+
+ ret = 0;
+err:
+ WARN_ON(ret); /* we assume that errors will be very rare */
+ dw_pcie_dbi_ro_wr_dis(pci);
+ return ret;
}
static int fu740_pcie_host_init(struct pcie_port *pp)
@@ -259,11 +308,11 @@ static int fu740_pcie_probe(struct platform_device *pdev)
return PTR_ERR(afp->mgmt_base);
/* Fetch GPIOs */
- afp->reset = devm_gpiod_get_optional(dev, "reset-gpios", GPIOD_OUT_LOW);
+ afp->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(afp->reset))
return dev_err_probe(dev, PTR_ERR(afp->reset), "unable to get reset-gpios\n");
- afp->pwren = devm_gpiod_get_optional(dev, "pwren-gpios", GPIOD_OUT_LOW);
+ afp->pwren = devm_gpiod_get_optional(dev, "pwren", GPIOD_OUT_LOW);
if (IS_ERR(afp->pwren))
return dev_err_probe(dev, PTR_ERR(afp->pwren), "unable to get pwren-gpios\n");