diff options
author | Marek Vasut <marek.vasut+renesas@mailbox.org> | 2025-06-17 10:16:27 +0200 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2025-06-27 08:25:56 -0600 |
commit | 4600b59568339602ef0c95377cc64f561132ebea (patch) | |
tree | 6323d6f641357d0ceb583794a7af5a730cc6636f | |
parent | 5d7e003248ae836cbcc2b4c254901c1d85c85537 (diff) |
drivers: pci: pcie_dw_common: Add dw_pcie_link_set_max_link_width()
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 <marek.vasut+renesas@mailbox.org>
-rw-r--r-- | drivers/pci/pcie_dw_common.c | 45 | ||||
-rw-r--r-- | drivers/pci/pcie_dw_common.h | 2 |
2 files changed, 47 insertions, 0 deletions
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); |