summaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/Kconfig10
-rw-r--r--drivers/pci/Makefile1
-rw-r--r--drivers/pci/pci-rcar-gen4.c565
-rw-r--r--drivers/pci/pcie_dw_common.c45
-rw-r--r--drivers/pci/pcie_dw_common.h9
-rw-r--r--drivers/pci/pcie_dw_meson.c6
-rw-r--r--drivers/pci/pcie_dw_qcom.c19
-rw-r--r--drivers/pci/pcie_dw_rockchip.c39
8 files changed, 633 insertions, 61 deletions
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 409049137cc..8ffd88c722d 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -389,6 +389,16 @@ config PCIE_DW_QCOM
Say Y here if you want to enable DW PCIe controller support on
Qualcomm SoCs.
+config PCI_RCAR_GEN4
+ bool "Renesas R-Car Gen4 PCIe driver"
+ depends on RCAR_GEN4
+ select DM_RESET
+ select DM_GPIO
+ select PCIE_DW_COMMON
+ help
+ Say Y here if you want to enable PCIe controller support on
+ Renesas R-Car Gen4 SoCs.
+
config PCIE_ROCKCHIP
bool "Enable Rockchip PCIe driver"
depends on ARCH_ROCKCHIP
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index ba53f594963..a0420e733ed 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_PCIE_IMX) += pcie_imx.o
obj-$(CONFIG_PCI_MVEBU) += pci_mvebu.o
obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
obj-$(CONFIG_PCI_RCAR_GEN3) += pci-rcar-gen3.o
+obj-$(CONFIG_PCI_RCAR_GEN4) += pci-rcar-gen4.o
obj-$(CONFIG_SH7751_PCI) +=pci_sh7751.o
obj-$(CONFIG_PCI_TEGRA) += pci_tegra.o
obj-$(CONFIG_PCIE_IPROC) += pcie_iproc.o
diff --git a/drivers/pci/pci-rcar-gen4.c b/drivers/pci/pci-rcar-gen4.c
new file mode 100644
index 00000000000..87cd69f989d
--- /dev/null
+++ b/drivers/pci/pci-rcar-gen4.c
@@ -0,0 +1,565 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PCIe controller driver for Renesas R-Car Gen4 Series SoCs
+ * Copyright (C) 2025 Marek Vasut <marek.vasut+renesas@mailbox.org>
+ * Based on Linux kernel driver
+ * Copyright (C) 2022-2023 Renesas Electronics Corporation
+ *
+ * The r8a779g0 (R-Car V4H) controller requires a specific firmware to be
+ * provided, to initialize the PHY. Otherwise, the PCIe controller will not
+ * work.
+ */
+
+#include <asm-generic/gpio.h>
+#include <asm/arch/gpio.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <clk.h>
+#include <command.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <env.h>
+#include <log.h>
+#include <reset.h>
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+
+#include "pcie_dw_common.h"
+
+/* Renesas-specific */
+/* PCIe Mode Setting Register 0 */
+#define PCIEMSR0 0x0000
+#define APP_SRIS_MODE BIT(6)
+#define DEVICE_TYPE_EP 0
+#define DEVICE_TYPE_RC BIT(4)
+#define BIFUR_MOD_SET_ON BIT(0)
+
+/* PCIe Interrupt Status 0 */
+#define PCIEINTSTS0 0x0084
+
+/* PCIe Interrupt Status 0 Enable */
+#define PCIEINTSTS0EN 0x0310
+#define MSI_CTRL_INT BIT(26)
+#define SMLH_LINK_UP BIT(7)
+#define RDLH_LINK_UP BIT(6)
+
+/* PCIe DMA Interrupt Status Enable */
+#define PCIEDMAINTSTSEN 0x0314
+#define PCIEDMAINTSTSEN_INIT GENMASK(15, 0)
+
+/* Port Logic Registers 89 */
+#define PRTLGC89 0x0b70
+
+/* Port Logic Registers 90 */
+#define PRTLGC90 0x0b74
+
+/* PCIe Reset Control Register 1 */
+#define PCIERSTCTRL1 0x0014
+#define APP_HOLD_PHY_RST BIT(16)
+#define APP_LTSSM_ENABLE BIT(0)
+
+/* PCIe Power Management Control */
+#define PCIEPWRMNGCTRL 0x0070
+#define APP_CLK_REQ_N BIT(11)
+#define APP_CLK_PM_EN BIT(10)
+
+#define RCAR_NUM_SPEED_CHANGE_RETRIES 10
+#define RCAR_MAX_LINK_SPEED 4
+
+#define RCAR_GEN4_PCIE_EP_FUNC_DBI_OFFSET 0x1000
+#define RCAR_GEN4_PCIE_EP_FUNC_DBI2_OFFSET 0x800
+
+#define RCAR_GEN4_PCIE_FIRMWARE_NAME "rcar_gen4_pcie.bin"
+#define RCAR_GEN4_PCIE_FIRMWARE_BASE_ADDR 0xc000
+
+#define PCIE_T_PVPERL_MS 100
+
+/**
+ * struct rcar_gen4_pcie - Renesas R-Car Gen4 DW PCIe controller state
+ *
+ * @rcar: The common PCIe DW structure
+ * @pwr_rst: The PWR reset of the PCIe core
+ * @core_clk: The core clock of the PCIe core
+ * @ref_clk: The reference clock of the PCIe core and possibly bus
+ * @pe_rst: PERST GPIO
+ * @app_base: The base address of application register space
+ * @dbi2_base: The base address of DBI2 register space
+ * @phy_base: The base address of PHY register space
+ * @max_link_speed: Maximum PCIe link speed supported by the setup
+ * @num_lanes: Number of PCIe lanes used by the setup
+ * @firmware: PHY firmware
+ * @firmware_size: PHY firmware size in Bytes
+ */
+struct rcar_gen4_pcie {
+ /* Must be first member of the struct */
+ struct pcie_dw dw;
+ struct reset_ctl pwr_rst;
+ struct clk *core_clk;
+ struct clk *ref_clk;
+ struct gpio_desc pe_rst;
+ void *app_base;
+ void *dbi2_base;
+ void *phy_base;
+ u32 max_link_speed;
+ u32 num_lanes;
+ u16 *firmware;
+ u32 firmware_size;
+};
+
+/* Common */
+static bool rcar_gen4_pcie_link_up(struct rcar_gen4_pcie *rcar)
+{
+ u32 val, mask;
+
+ val = readl(rcar->app_base + PCIEINTSTS0);
+ mask = RDLH_LINK_UP | SMLH_LINK_UP;
+
+ return (val & mask) == mask;
+}
+
+/*
+ * Manually initiate the speed change. Return 0 if change succeeded; otherwise
+ * -ETIMEDOUT.
+ */
+static int rcar_gen4_pcie_speed_change(struct rcar_gen4_pcie *rcar)
+{
+ u32 val;
+ int i;
+
+ clrbits_le32(rcar->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL,
+ PORT_LOGIC_SPEED_CHANGE);
+
+ setbits_le32(rcar->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL,
+ PORT_LOGIC_SPEED_CHANGE);
+
+ for (i = 0; i < RCAR_NUM_SPEED_CHANGE_RETRIES; i++) {
+ val = readl(rcar->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+ if (!(val & PORT_LOGIC_SPEED_CHANGE))
+ return 0;
+ mdelay(10);
+ }
+
+ return -ETIMEDOUT;
+}
+
+/*
+ * SoC datasheet suggests checking port logic register bits during firmware
+ * write. If read returns non-zero value, then this function returns -EAGAIN
+ * indicating that the write needs to be done again. If read returns zero,
+ * then return 0 to indicate success.
+ */
+static int rcar_gen4_pcie_reg_test_bit(struct rcar_gen4_pcie *rcar,
+ u32 offset, u32 mask)
+{
+ if (readl(rcar->dw.dbi_base + offset) & mask)
+ return -EAGAIN;
+
+ return 0;
+}
+
+static int rcar_gen4_pcie_download_phy_firmware(struct rcar_gen4_pcie *rcar)
+{
+ /* The check_addr values are magical numbers in the datasheet */
+ static const u32 check_addr[] = {
+ 0x00101018,
+ 0x00101118,
+ 0x00101021,
+ 0x00101121,
+ };
+ unsigned int i, timeout;
+ u32 data;
+ int ret;
+
+ for (i = 0; i < rcar->firmware_size / 2; i++) {
+ data = rcar->firmware[i];
+ timeout = 100;
+ do {
+ writel(RCAR_GEN4_PCIE_FIRMWARE_BASE_ADDR + i, rcar->dw.dbi_base + PRTLGC89);
+ writel(data, rcar->dw.dbi_base + PRTLGC90);
+ if (!rcar_gen4_pcie_reg_test_bit(rcar, PRTLGC89, BIT(30)))
+ break;
+ if (!(--timeout))
+ return -ETIMEDOUT;
+ udelay(100);
+ } while (1);
+ }
+
+ setbits_le32(rcar->phy_base + 0x0f8, BIT(17));
+
+ for (i = 0; i < ARRAY_SIZE(check_addr); i++) {
+ timeout = 100;
+ do {
+ writel(check_addr[i], rcar->dw.dbi_base + PRTLGC89);
+ ret = rcar_gen4_pcie_reg_test_bit(rcar, PRTLGC89, BIT(30));
+ ret |= rcar_gen4_pcie_reg_test_bit(rcar, PRTLGC90, BIT(0));
+ if (!ret)
+ break;
+ if (!(--timeout))
+ return -ETIMEDOUT;
+ udelay(100);
+ } while (1);
+ }
+
+ return ret;
+}
+
+static int rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable)
+{
+ u32 val;
+ int ret;
+
+ if (!enable) {
+ clrbits_le32(rcar->app_base + PCIERSTCTRL1, APP_LTSSM_ENABLE);
+ return 0;
+ }
+
+ setbits_le32(rcar->dw.dbi_base + PCIE_PORT_FORCE,
+ PORT_FORCE_DO_DESKEW_FOR_SRIS);
+
+ setbits_le32(rcar->app_base + PCIEMSR0, APP_SRIS_MODE);
+
+ /*
+ * The R-Car Gen4 datasheet doesn't describe the PHY registers' name.
+ * But, the initialization procedure describes these offsets. So,
+ * this driver has magical offset numbers.
+ */
+ clrsetbits_le32(rcar->phy_base + 0x700, BIT(28), 0);
+ clrsetbits_le32(rcar->phy_base + 0x700, BIT(20), 0);
+ clrsetbits_le32(rcar->phy_base + 0x700, BIT(12), 0);
+ clrsetbits_le32(rcar->phy_base + 0x700, BIT(4), 0);
+
+ clrsetbits_le32(rcar->phy_base + 0x148, GENMASK(23, 22), BIT(22));
+ clrsetbits_le32(rcar->phy_base + 0x148, GENMASK(18, 16), GENMASK(17, 16));
+ clrsetbits_le32(rcar->phy_base + 0x148, GENMASK(7, 6), BIT(6));
+ clrsetbits_le32(rcar->phy_base + 0x148, GENMASK(2, 0), GENMASK(11, 0));
+ clrsetbits_le32(rcar->phy_base + 0x1d4, GENMASK(16, 15), GENMASK(16, 15));
+ clrsetbits_le32(rcar->phy_base + 0x514, BIT(26), BIT(26));
+ clrsetbits_le32(rcar->phy_base + 0x0f8, BIT(16), 0);
+ clrsetbits_le32(rcar->phy_base + 0x0f8, BIT(19), BIT(19));
+
+ clrbits_le32(rcar->app_base + PCIERSTCTRL1, APP_HOLD_PHY_RST);
+
+ ret = readl_poll_timeout(rcar->phy_base + 0x0f8, val, !(val & BIT(18)), 10000);
+ if (ret < 0)
+ return ret;
+
+ ret = rcar_gen4_pcie_download_phy_firmware(rcar);
+ if (ret)
+ return ret;
+
+ setbits_le32(rcar->app_base + PCIERSTCTRL1, APP_LTSSM_ENABLE);
+
+ return 0;
+}
+
+/*
+ * Enable LTSSM of this controller and manually initiate the speed change.
+ * Always return 0.
+ */
+static int rcar_gen4_pcie_start_link(struct rcar_gen4_pcie *rcar)
+{
+ int i, ret;
+
+ ret = rcar_gen4_pcie_ltssm_control(rcar, true);
+ if (ret)
+ return ret;
+
+ /*
+ * Require direct speed change with retrying here if the max_link_speed
+ * is PCIe Gen2 or higher.
+ */
+ if (rcar->max_link_speed == LINK_SPEED_GEN_1)
+ return 0;
+
+ for (i = 0; i < RCAR_MAX_LINK_SPEED; i++) {
+ /* It may not be connected in EP mode yet. So, break the loop */
+ if (rcar_gen4_pcie_speed_change(rcar))
+ break;
+ }
+
+ return 0;
+}
+
+static void rcar_gen4_pcie_additional_common_init(struct rcar_gen4_pcie *rcar)
+{
+ clrsetbits_le32(rcar->dw.dbi_base + PCIE_PORT_LANE_SKEW,
+ PORT_LANE_SKEW_INSERT_MASK,
+ (rcar->num_lanes < 4) ? BIT(6) : 0);
+
+ setbits_le32(rcar->app_base + PCIEPWRMNGCTRL,
+ APP_CLK_REQ_N | APP_CLK_PM_EN);
+}
+
+static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
+{
+ int ret;
+
+ ret = clk_prepare_enable(rcar->core_clk);
+ if (ret)
+ return ret;
+
+ ret = reset_assert(&rcar->pwr_rst);
+ if (ret)
+ goto err_unprepare;
+
+ setbits_le32(rcar->app_base + PCIEMSR0,
+ DEVICE_TYPE_RC |
+ ((rcar->num_lanes < 4) ? BIFUR_MOD_SET_ON : 0));
+
+ ret = reset_deassert(&rcar->pwr_rst);
+ if (ret)
+ goto err_unprepare;
+
+ rcar_gen4_pcie_additional_common_init(rcar);
+
+ return 0;
+
+err_unprepare:
+ clk_disable_unprepare(rcar->core_clk);
+
+ return ret;
+}
+
+/* Host mode */
+static int rcar_gen4_pcie_host_init(struct udevice *dev)
+{
+ struct rcar_gen4_pcie *rcar = dev_get_priv(dev);
+ int ret;
+
+ dm_gpio_set_value(&rcar->pe_rst, 1);
+
+ ret = rcar_gen4_pcie_common_init(rcar);
+ if (ret)
+ return ret;
+
+ /*
+ * According to the section 3.5.7.2 "RC Mode" in DWC PCIe Dual Mode
+ * Rev.5.20a and 3.5.6.1 "RC mode" in DWC PCIe RC databook v5.20a, we
+ * should disable two BARs to avoid unnecessary memory assignment
+ * during device enumeration.
+ */
+ writel(0x0, rcar->dbi2_base + PCI_BASE_ADDRESS_0);
+ writel(0x0, rcar->dbi2_base + PCI_BASE_ADDRESS_1);
+
+ /* Disable MSI interrupt signal */
+ clrbits_le32(rcar->app_base + PCIEINTSTS0EN, MSI_CTRL_INT);
+
+ mdelay(PCIE_T_PVPERL_MS); /* pe_rst requires 100msec delay */
+
+ dm_gpio_set_value(&rcar->pe_rst, 0);
+
+ return 0;
+}
+
+static int rcar_gen4_pcie_load_firmware(struct rcar_gen4_pcie *rcar)
+{
+ ulong addr, size;
+ int ret;
+
+ /*
+ * Run user specified firmware loading script, which loads the
+ * firmware from whichever location the user decides it should
+ * load the firmware from, by whatever means the user decides.
+ */
+ ret = run_command_list("run renesas_rcar_gen4_load_firmware", -1, 0);
+ if (ret) {
+ printf("Firmware loading script 'renesas_rcar_gen4_load_firmware' not defined or failed.\n");
+ goto fail;
+ }
+
+ /* Find out where the firmware got loaded and how long it is. */
+ addr = env_get_hex("renesas_rcar_gen4_load_firmware_addr", 0);
+ size = env_get_hex("renesas_rcar_gen4_load_firmware_size", 0);
+
+ /*
+ * Clear the variables set by the firmware loading script, as
+ * their content would become stale once this function exits.
+ */
+ env_set("renesas_rcar_gen4_load_firmware_addr", NULL);
+ env_set("renesas_rcar_gen4_load_firmware_size", NULL);
+
+ if (!addr || !size) {
+ printf("Firmware address (%lx) or size (%lx) are invalid.\n", addr, size);
+ goto fail;
+ }
+
+ /* Create local copy of the loaded firmware. */
+ rcar->firmware = (u16 *)memdup((void *)addr, size);
+ if (!rcar->firmware)
+ return -ENOMEM;
+
+ rcar->firmware_size = size;
+
+ return 0;
+
+fail:
+ printf("Define 'renesas_rcar_gen4_load_firmware' script which loads the R-Car\n"
+ "Gen4 PCIe controller firmware from storage into memory and sets these\n"
+ "two environment variables:\n"
+ " renesas_rcar_gen4_load_firmware_addr ... address of firmware in memory\n"
+ " renesas_rcar_gen4_load_firmware_size ... length of firmware in bytes\n"
+ "\n"
+ "Example:\n"
+ " => env set renesas_rcar_gen4_load_firmware 'env set renesas_rcar_gen4_load_firmware_addr 0x54000000 && load mmc 0:1 ${renesas_rcar_gen4_load_firmware_addr} lib/firmware/rcar_gen4_pcie.bin && env set renesas_rcar_gen4_load_firmware_size ${filesize}'\n"
+ );
+ return -EINVAL;
+}
+
+/**
+ * rcar_gen4_pcie_probe() - Probe the PCIe bus for active link
+ *
+ * @dev: A pointer to the device being operated on
+ *
+ * Probe for an active link on the PCIe bus and configure the controller
+ * to enable this port.
+ *
+ * Return: 0 on success, else -ENODEV
+ */
+static int rcar_gen4_pcie_probe(struct udevice *dev)
+{
+ struct rcar_gen4_pcie *rcar = dev_get_priv(dev);
+ struct udevice *ctlr = pci_get_controller(dev);
+ struct pci_controller *hose = dev_get_uclass_priv(ctlr);
+ int ret;
+
+ ret = rcar_gen4_pcie_load_firmware(rcar);
+ if (ret)
+ return ret;
+
+ rcar->dw.first_busno = dev_seq(dev);
+ rcar->dw.dev = dev;
+
+ ret = reset_get_by_name(dev, "pwr", &rcar->pwr_rst);
+ if (ret)
+ return ret;
+
+ rcar->core_clk = devm_clk_get(dev, "core");
+ if (IS_ERR(rcar->core_clk))
+ return PTR_ERR(rcar->core_clk);
+
+ rcar->ref_clk = devm_clk_get(dev, "ref");
+ if (IS_ERR(rcar->ref_clk))
+ return PTR_ERR(rcar->ref_clk);
+
+ ret = clk_prepare_enable(rcar->ref_clk);
+ if (ret)
+ return ret;
+
+ ret = gpio_request_by_name(dev, "reset-gpios", 0, &rcar->pe_rst,
+ GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
+ if (ret)
+ return ret;
+
+ ret = rcar_gen4_pcie_host_init(dev);
+ if (ret)
+ return ret;
+
+ pcie_dw_setup_host(&rcar->dw);
+
+ dw_pcie_dbi_write_enable(&rcar->dw, true);
+
+ dw_pcie_link_set_max_link_width(&rcar->dw, rcar->num_lanes);
+
+ ret = rcar_gen4_pcie_start_link(rcar);
+ if (ret)
+ return ret;
+
+ dw_pcie_dbi_write_enable(&rcar->dw, false);
+
+ if (!rcar_gen4_pcie_link_up(rcar)) {
+ printf("PCIE-%d: Link down\n", dev_seq(dev));
+ return -ENODEV;
+ }
+
+ printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", dev_seq(dev),
+ pcie_dw_get_link_speed(&rcar->dw),
+ pcie_dw_get_link_width(&rcar->dw),
+ hose->first_busno);
+
+ pcie_dw_prog_outbound_atu_unroll(&rcar->dw, PCIE_ATU_REGION_INDEX0,
+ PCIE_ATU_TYPE_MEM,
+ rcar->dw.mem.phys_start,
+ rcar->dw.mem.bus_start, rcar->dw.mem.size);
+
+ return 0;
+}
+
+/**
+ * rcar_gen4_pcie_of_to_plat() - Translate from DT to device state
+ *
+ * @dev: A pointer to the device being operated on
+ *
+ * Translate relevant data from the device tree pertaining to device @dev into
+ * state that the driver will later make use of. This state is stored in the
+ * device's private data structure.
+ *
+ * Return: 0 on success, else -EINVAL
+ */
+static int rcar_gen4_pcie_of_to_plat(struct udevice *dev)
+{
+ struct rcar_gen4_pcie *rcar = dev_get_priv(dev);
+
+ /* Get the controller base address */
+ rcar->dw.dbi_base = (void *)dev_read_addr_name(dev, "dbi");
+ if ((fdt_addr_t)rcar->dw.dbi_base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ /* Get the config space base address and size */
+ rcar->dw.cfg_base = (void *)dev_read_addr_size_name(dev, "config",
+ &rcar->dw.cfg_size);
+ if ((fdt_addr_t)rcar->dw.cfg_base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ /* Get the iATU base address and size */
+ rcar->dw.atu_base = (void *)dev_read_addr_name(dev, "atu");
+ if ((fdt_addr_t)rcar->dw.atu_base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ /* Get the PHY base address and size */
+ rcar->phy_base = (void *)dev_read_addr_name(dev, "phy");
+ if ((fdt_addr_t)rcar->phy_base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ /* Get the app base address and size */
+ rcar->app_base = (void *)dev_read_addr_name(dev, "app");
+ if ((fdt_addr_t)rcar->app_base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ /* Get the dbi2 base address and size */
+ rcar->dbi2_base = (void *)dev_read_addr_name(dev, "dbi2");
+ if ((fdt_addr_t)rcar->dbi2_base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ rcar->max_link_speed =
+ clamp(dev_read_u32_default(dev, "max-link-speed",
+ LINK_SPEED_GEN_4),
+ LINK_SPEED_GEN_1, RCAR_MAX_LINK_SPEED);
+
+ rcar->num_lanes = dev_read_u32_default(dev, "num-lanes", 4);
+
+ return 0;
+}
+
+static const struct dm_pci_ops rcar_gen4_pcie_ops = {
+ .read_config = pcie_dw_read_config,
+ .write_config = pcie_dw_write_config,
+};
+
+static const struct udevice_id rcar_gen4_pcie_ids[] = {
+ { .compatible = "renesas,rcar-gen4-pcie" },
+ { }
+};
+
+U_BOOT_DRIVER(rcar_gen4_pcie) = {
+ .name = "rcar_gen4_pcie",
+ .id = UCLASS_PCI,
+ .of_match = rcar_gen4_pcie_ids,
+ .ops = &rcar_gen4_pcie_ops,
+ .of_to_plat = rcar_gen4_pcie_of_to_plat,
+ .probe = rcar_gen4_pcie_probe,
+ .priv_auto = sizeof(struct rcar_gen4_pcie),
+};
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..5fa50f3dc3a 100644
--- a/drivers/pci/pcie_dw_common.h
+++ b/drivers/pci/pcie_dw_common.h
@@ -66,8 +66,12 @@
#define LINK_SPEED_GEN_1 0x1
#define LINK_SPEED_GEN_2 0x2
#define LINK_SPEED_GEN_3 0x3
+#define LINK_SPEED_GEN_4 0x4
/* Synopsys-specific PCIe configuration registers */
+#define PCIE_PORT_FORCE 0x708
+#define PORT_FORCE_DO_DESKEW_FOR_SRIS BIT(23)
+
#define PCIE_PORT_LINK_CONTROL 0x710
#define PORT_LINK_DLL_LINK_EN BIT(5)
#define PORT_LINK_FAST_LINK_MODE BIT(7)
@@ -78,6 +82,9 @@
#define PORT_LINK_MODE_4_LANES PORT_LINK_MODE(0x7)
#define PORT_LINK_MODE_8_LANES PORT_LINK_MODE(0xf)
+#define PCIE_PORT_LANE_SKEW 0x714
+#define PORT_LANE_SKEW_INSERT_MASK GENMASK(23, 0)
+
#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
#define PORT_LOGIC_N_FTS_MASK GENMASK(7, 0)
#define PORT_LOGIC_SPEED_CHANGE BIT(17)
@@ -130,6 +137,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);
diff --git a/drivers/pci/pcie_dw_meson.c b/drivers/pci/pcie_dw_meson.c
index bb78e7874b1..483b07ce078 100644
--- a/drivers/pci/pcie_dw_meson.c
+++ b/drivers/pci/pcie_dw_meson.c
@@ -115,13 +115,9 @@ static void meson_pcie_configure(struct meson_pcie *priv)
val &= ~PORT_LINK_FAST_LINK_MODE;
val |= PORT_LINK_DLL_LINK_EN;
val &= ~PORT_LINK_MODE_MASK;
- val |= PORT_LINK_MODE_1_LANES;
writel(val, priv->dw.dbi_base + PCIE_PORT_LINK_CONTROL);
- val = readl(priv->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
- val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
- val |= PORT_LOGIC_LINK_WIDTH_1_LANES;
- writel(val, priv->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+ dw_pcie_link_set_max_link_width(&priv->dw, 1);
dw_pcie_dbi_write_enable(&priv->dw, false);
}
diff --git a/drivers/pci/pcie_dw_qcom.c b/drivers/pci/pcie_dw_qcom.c
index 39b4cd4efe2..978754e8472 100644
--- a/drivers/pci/pcie_dw_qcom.c
+++ b/drivers/pci/pcie_dw_qcom.c
@@ -213,17 +213,6 @@ static void qcom_pcie_clear_hpc(struct qcom_pcie *priv)
dw_pcie_dbi_write_enable(&priv->dw, false);
}
-static void qcom_pcie_set_lanes(struct qcom_pcie *priv, unsigned int lanes)
-{
- u8 offset = pcie_dw_find_capability(&priv->dw, PCI_CAP_ID_EXP);
- u32 val;
-
- val = readl(priv->dw.dbi_base + offset + PCI_EXP_LNKCAP);
- val &= ~PCI_EXP_LNKCAP_MLW;
- val |= FIELD_PREP(PCI_EXP_LNKCAP_MLW, lanes);
- writel(val, priv->dw.dbi_base + offset + PCI_EXP_LNKCAP);
-}
-
static int qcom_pcie_config_sid_1_9_0(struct qcom_pcie *priv)
{
/* iommu map structure */
@@ -299,15 +288,9 @@ static void qcom_pcie_configure(struct qcom_pcie *priv)
val &= ~PORT_LINK_FAST_LINK_MODE;
val |= PORT_LINK_DLL_LINK_EN;
val &= ~PORT_LINK_MODE_MASK;
- val |= PORT_LINK_MODE_2_LANES;
writel(val, priv->dw.dbi_base + PCIE_PORT_LINK_CONTROL);
- val = readl(priv->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
- val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
- val |= PORT_LOGIC_LINK_WIDTH_2_LANES;
- writel(val, priv->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
-
- qcom_pcie_set_lanes(priv, 2);
+ dw_pcie_link_set_max_link_width(&priv->dw, 2);
dw_pcie_dbi_write_enable(&priv->dw, false);
}
diff --git a/drivers/pci/pcie_dw_rockchip.c b/drivers/pci/pcie_dw_rockchip.c
index ac7faa4cc19..208aa30463a 100644
--- a/drivers/pci/pcie_dw_rockchip.c
+++ b/drivers/pci/pcie_dw_rockchip.c
@@ -158,8 +158,6 @@ static inline void rk_pcie_writel_apb(struct rk_pcie *rk_pcie, u32 reg,
*/
static void rk_pcie_configure(struct rk_pcie *pci)
{
- u32 val;
-
dw_pcie_dbi_write_enable(&pci->dw, true);
/* Disable BAR 0 and BAR 1 */
@@ -175,43 +173,8 @@ static void rk_pcie_configure(struct rk_pcie *pci)
TARGET_LINK_SPEED_MASK, pci->gen);
/* Set the number of lanes */
- val = readl(pci->dw.dbi_base + PCIE_PORT_LINK_CONTROL);
- val &= ~PORT_LINK_FAST_LINK_MODE;
- val |= PORT_LINK_DLL_LINK_EN;
- val &= ~PORT_LINK_MODE_MASK;
- switch (pci->num_lanes) {
- case 1:
- val |= PORT_LINK_MODE_1_LANES;
- break;
- case 2:
- val |= PORT_LINK_MODE_2_LANES;
- break;
- case 4:
- val |= PORT_LINK_MODE_4_LANES;
- break;
- default:
- dev_err(pci->dw.dev, "num-lanes %u: invalid value\n", pci->num_lanes);
- goto out;
- }
- writel(val, pci->dw.dbi_base + PCIE_PORT_LINK_CONTROL);
-
- /* Set link width speed control register */
- val = readl(pci->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
- val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
- switch (pci->num_lanes) {
- case 1:
- val |= PORT_LOGIC_LINK_WIDTH_1_LANES;
- break;
- case 2:
- val |= PORT_LOGIC_LINK_WIDTH_2_LANES;
- break;
- case 4:
- val |= PORT_LOGIC_LINK_WIDTH_4_LANES;
- break;
- }
- writel(val, pci->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+ dw_pcie_link_set_max_link_width(&pci->dw, pci->num_lanes);
-out:
dw_pcie_dbi_write_enable(&pci->dw, false);
}