Add dw_pcie_link_set_max_link_width() implementation ported from Linux kernel
as of commit 89db0793c9f2 ("PCI: dwc: Add missing PCI_EXP_LNKCAP_MLW handling").
This is common code which is already duplicated in multiple drivers.

Signed-off-by: Marek Vasut <[email protected]>
---
Cc: Casey Connolly <[email protected]>
Cc: Christian Marangi <[email protected]>
Cc: Daniel Schwierzeck <[email protected]>
Cc: Jiaxun Yang <[email protected]>
Cc: John Crispin <[email protected]>
Cc: Kever Yang <[email protected]>
Cc: Neil Armstrong <[email protected]>
Cc: Nobuhiro Iwamatsu <[email protected]>
Cc: Philipp Tomsich <[email protected]>
Cc: Siddharth Vadapalli <[email protected]>
Cc: Simon Glass <[email protected]>
Cc: Sumit Garg <[email protected]>
Cc: Tom Rini <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
---
 drivers/pci/pcie_dw_common.c | 45 ++++++++++++++++++++++++++++++++++++
 drivers/pci/pcie_dw_common.h |  2 ++
 2 files changed, 47 insertions(+)

diff --git a/drivers/pci/pcie_dw_common.c b/drivers/pci/pcie_dw_common.c
index 78961271a8e..c4cad019373 100644
--- a/drivers/pci/pcie_dw_common.c
+++ b/drivers/pci/pcie_dw_common.c
@@ -13,6 +13,7 @@
 #include <pci.h>
 #include <dm/device_compat.h>
 #include <asm/io.h>
+#include <linux/bitfield.h>
 #include <linux/delay.h>
 #include "pcie_dw_common.h"
 
@@ -28,6 +29,50 @@ int pcie_dw_get_link_width(struct pcie_dw *pci)
                PCIE_LINK_STATUS_WIDTH_MASK) >> PCIE_LINK_STATUS_WIDTH_OFF;
 }
 
+void dw_pcie_link_set_max_link_width(struct pcie_dw *pci, u32 num_lanes)
+{
+       u32 lnkcap, lwsc, plc;
+       u8 cap;
+
+       if (!num_lanes)
+               return;
+
+       /* Set the number of lanes */
+       plc = readl(pci->dbi_base + PCIE_PORT_LINK_CONTROL);
+       plc &= ~PORT_LINK_FAST_LINK_MODE;
+       plc &= ~PORT_LINK_MODE_MASK;
+
+       /* Set link width speed control register */
+       lwsc = readl(pci->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+       lwsc &= ~PORT_LOGIC_LINK_WIDTH_MASK;
+       lwsc |= PORT_LOGIC_LINK_WIDTH_1_LANES;
+       switch (num_lanes) {
+       case 1:
+               plc |= PORT_LINK_MODE_1_LANES;
+               break;
+       case 2:
+               plc |= PORT_LINK_MODE_2_LANES;
+               break;
+       case 4:
+               plc |= PORT_LINK_MODE_4_LANES;
+               break;
+       case 8:
+               plc |= PORT_LINK_MODE_8_LANES;
+               break;
+       default:
+               dev_err(pci->dev, "num-lanes %u: invalid value\n", num_lanes);
+               return;
+       }
+       writel(plc, pci->dbi_base + PCIE_PORT_LINK_CONTROL);
+       writel(lwsc, pci->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+
+       cap = pcie_dw_find_capability(pci, PCI_CAP_ID_EXP);
+       lnkcap = readl(pci->dbi_base + cap + PCI_EXP_LNKCAP);
+       lnkcap &= ~PCI_EXP_LNKCAP_MLW;
+       lnkcap |= FIELD_PREP(PCI_EXP_LNKCAP_MLW, num_lanes);
+       writel(lnkcap, pci->dbi_base + cap + PCI_EXP_LNKCAP);
+}
+
 static void dw_pcie_writel_ob_unroll(struct pcie_dw *pci, u32 index, u32 reg,
                                     u32 val)
 {
diff --git a/drivers/pci/pcie_dw_common.h b/drivers/pci/pcie_dw_common.h
index 8cb99a12ea1..ccd423081eb 100644
--- a/drivers/pci/pcie_dw_common.h
+++ b/drivers/pci/pcie_dw_common.h
@@ -130,6 +130,8 @@ int pcie_dw_get_link_speed(struct pcie_dw *pci);
 
 int pcie_dw_get_link_width(struct pcie_dw *pci);
 
+void dw_pcie_link_set_max_link_width(struct pcie_dw *pci, u32 num_lanes);
+
 int pcie_dw_prog_outbound_atu_unroll(struct pcie_dw *pci, int index, int type, 
u64 cpu_addr,
                                     u64 pci_addr, u32 size);
 
-- 
2.47.2

Reply via email to