Add pcie driver for SiFive fu740, the driver depends on
fu740 gpio, clk and reset driver to do init. Force running at Gen1
for better capatible enumeration.

Several devices are tested:
a) M.2 NVMe SSD
b) USB-to-PCI adapter
c) Ethernet adapter (E1000 compatible)

Signed-off-by: Green Wan <green....@sifive.com>
---
 drivers/pci/Kconfig       |   9 +
 drivers/pci/Makefile      |   1 +
 drivers/pci/pcie_sifive.c | 797 ++++++++++++++++++++++++++++++++++++++
 drivers/pci/pcie_sifive.h | 374 ++++++++++++++++++
 4 files changed, 1181 insertions(+)
 create mode 100644 drivers/pci/pcie_sifive.c
 create mode 100644 drivers/pci/pcie_sifive.h

diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index ba41787f64..b078e7689e 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -97,6 +97,15 @@ config PCIE_DW_MVEBU
          Armada-8K SoCs. The PCIe controller on Armada-8K is based on
          DesignWare hardware.
 
+config PCIE_SIFIVE_FU740
+       bool "Enable SiFive FU740 PCIe"
+       depends on CLK_SIFIVE_PRCI
+       depends on RESET_SIFIVE
+       depends on SIFIVE_GPIO
+       help
+         Say Y here if you want to enable PCIe controller support on
+         FU740.
+
 config PCIE_FSL
        bool "FSL PowerPC PCIe support"
        depends on DM_PCI
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 5ed94bc95c..5400d59cc5 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -51,3 +51,4 @@ obj-$(CONFIG_PCIE_ROCKCHIP) += pcie_rockchip.o
 obj-$(CONFIG_PCIE_DW_ROCKCHIP) += pcie_dw_rockchip.o
 obj-$(CONFIG_PCI_BRCMSTB) += pcie_brcmstb.o
 obj-$(CONFIG_PCI_OCTEONTX) += pci_octeontx.o
+obj-$(CONFIG_PCIE_SIFIVE_FU740) += pcie_sifive.o
diff --git a/drivers/pci/pcie_sifive.c b/drivers/pci/pcie_sifive.c
new file mode 100644
index 0000000000..ada60876bc
--- /dev/null
+++ b/drivers/pci/pcie_sifive.c
@@ -0,0 +1,797 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * SiFive FU740 DesignWare PCIe Controller
+ *
+ * Copyright (C) 2020-2021 SiFive, Inc.
+ *
+ * Based in early part on the i.MX6 PCIe host controller shim which is:
+ *
+ * Copyright (C) 2013 Kosagi
+ *             http://www.kosagi.com
+ *
+ * Based on driver from author: Alan Mikhak <amik...@wirelessfabric.com>
+ */
+#include "pcie_sifive.h"
+#include <common.h>
+#include <dm.h>
+
+/* Host Bridge Identification */
+#define DEVICE_NAME    "SiFive FU740 PCIe Host Controller"
+#define VENDOR_ID      0x51fe
+#define DEVICE_ID      0x51fe
+
+static enum pcie_sifive_devtype pcie_sifive_get_devtype(struct pcie_sifive *sv)
+{
+       u32 val;
+
+       val = readl(sv->priv.iobase + MGMT_MISC_DEVICE_TYPE_OFFSET);
+       switch (val) {
+       case MGMT_MISC_DEVICE_TYPE_RC:
+               return SV_PCIE_HOST_TYPE;
+       case MGMT_MISC_DEVICE_TYPE_EP:
+               return SV_PCIE_ENDPOINT_TYPE;
+       default:
+               return SV_PCIE_UNKNOWN_TYPE;
+       }
+}
+
+static void pcie_sifive_priv_set_state(struct pcie_sifive *sv, u32 reg,
+                                      u32 bits, int state)
+{
+       u32 val;
+
+       val = readl(sv->priv.iobase + reg);
+       val = state ? (val | bits) : (val & !bits);
+       writel(val, sv->priv.iobase + reg);
+}
+
+static void pcie_sifive_assert_perstn(struct pcie_sifive *sv)
+{
+       dm_gpio_set_value(&sv->perstn_gpio, 0);
+       writel(0x0, sv->priv.iobase + PCIEX8MGMT_PERST_N);
+       mdelay(100);
+}
+
+static void pcie_sifive_power_on(struct pcie_sifive *sv)
+{
+       dm_gpio_set_value(&sv->pwren_gpio, 1);
+       mdelay(100);
+}
+
+static void pcie_sifive_deassert_perstn(struct pcie_sifive *sv)
+{
+       writel(0x1, sv->priv.iobase + PCIEX8MGMT_PERST_N);
+       dm_gpio_set_value(&sv->perstn_gpio, 1);
+       mdelay(100);
+}
+
+static int pcie_sifive_setphy(const u8 phy, const u8 write,
+                             const u16 addr, const u16 wrdata,
+                             u16 *rddata, struct pcie_sifive *sv)
+{
+       unsigned char ack = 0;
+
+       if (!(phy == 0 || phy == 1))
+               return -2;
+
+       /* setup phy para */
+       writel(addr, sv->priv.iobase +
+              (phy ? PCIEX8MGMT_PHY1_CR_PARA_ADDR :
+               PCIEX8MGMT_PHY0_CR_PARA_ADDR));
+
+       if (write)
+               writel(wrdata, sv->priv.iobase +
+                      (phy ? PCIEX8MGMT_PHY1_CR_PARA_WR_DATA :
+                       PCIEX8MGMT_PHY0_CR_PARA_WR_DATA));
+
+       /* enable access if write */
+       if (write)
+               writel(1, sv->priv.iobase +
+                      (phy ? PCIEX8MGMT_PHY1_CR_PARA_WR_EN :
+                       PCIEX8MGMT_PHY0_CR_PARA_WR_EN));
+       else
+               writel(1, sv->priv.iobase +
+                      (phy ? PCIEX8MGMT_PHY1_CR_PARA_RD_EN :
+                       PCIEX8MGMT_PHY0_CR_PARA_RD_EN));
+
+       /* wait for wait_idle */
+       do {
+               u32 val;
+
+               val = readl(sv->priv.iobase +
+                           (phy ? PCIEX8MGMT_PHY1_CR_PARA_ACK :
+                            PCIEX8MGMT_PHY0_CR_PARA_ACK));
+               if (val) {
+                       ack = 1;
+                       if (!write)
+                               readl(sv->priv.iobase +
+                                     (phy ? PCIEX8MGMT_PHY1_CR_PARA_RD_DATA :
+                                      PCIEX8MGMT_PHY0_CR_PARA_RD_DATA));
+                       mdelay(1);
+               }
+       } while (!ack);
+
+       /* clear */
+       if (write)
+               writel(0, sv->priv.iobase +
+                      (phy ? PCIEX8MGMT_PHY1_CR_PARA_WR_EN :
+                       PCIEX8MGMT_PHY0_CR_PARA_WR_EN));
+       else
+               writel(0, sv->priv.iobase +
+                      (phy ? PCIEX8MGMT_PHY1_CR_PARA_RD_EN :
+                       PCIEX8MGMT_PHY0_CR_PARA_RD_EN));
+
+       while (readl(sv->priv.iobase +
+                    (phy ? PCIEX8MGMT_PHY1_CR_PARA_ACK :
+                     PCIEX8MGMT_PHY0_CR_PARA_ACK))) {
+               /* wait for ~wait_idle */
+       }
+
+       return 0;
+}
+
+static void pcie_sifive_init_phy(struct pcie_sifive *sv)
+{
+       int lane;
+
+       /* enable phy cr_para_sel interfaces */
+       writel(0x1, sv->priv.iobase + PCIEX8MGMT_PHY0_CR_PARA_SEL);
+       writel(0x1, sv->priv.iobase + PCIEX8MGMT_PHY1_CR_PARA_SEL);
+       mdelay(1);
+
+       /* set PHY AC termination mode */
+       for (lane = 0; lane < PCIEX8MGMT_LANE_NUM; lane++) {
+               pcie_sifive_setphy(0, 1,
+                                  PCIEX8MGMT_LANE +
+                                  (PCIEX8MGMT_LANE_OFF * lane),
+                                  PCIEX8MGMT_TERM_MODE, NULL, sv);
+               pcie_sifive_setphy(1, 1,
+                                  PCIEX8MGMT_LANE +
+                                  (PCIEX8MGMT_LANE_OFF * lane),
+                                  PCIEX8MGMT_TERM_MODE, NULL, sv);
+       }
+}
+
+static void pcie_sifive_set_pci_int_pin(struct pcie_sifive *sv,
+                                       enum pci_interrupt_pin pin)
+{
+       u32 val;
+
+       /* ctrl_ro_wr_enable */
+       val = readl(sv->ctrl.iobase + PCIE_MISC_CONTROL_1);
+       val |= DBI_RO_WR_EN;
+       writel(val, sv->ctrl.iobase + PCIE_MISC_CONTROL_1);
+
+       writeb(pin, sv->ctrl.iobase + PCI_CONFIG(PCI_INTERRUPT_PIN));
+
+       /* ctrl_ro_wr_disable */
+       val &= ~DBI_RO_WR_EN;
+       writel(val, sv->ctrl.iobase + PCIE_MISC_CONTROL_1);
+}
+
+static int pcie_sifive_get_property(struct pcie_sifive *sv,
+                                   const char *property)
+{
+       u32 value = 0;
+
+       if (dev_read_u32(sv->pci.dev, property, &value))
+               return 0;
+
+       return value;
+}
+
+static int pcie_sifive_get_required_property(struct pcie_sifive *sv,
+                                            const char *property)
+{
+       int value;
+
+       value = pcie_sifive_get_property(sv, property);
+       if (value == -EINVAL)
+               sv_err(sv, "Unable to read %s property\n", property);
+
+       return value;
+}
+
+static u32 pcie_sifive_get_link_width_mask(struct pcie_sifive *sv, int lanes)
+{
+       switch (lanes) {
+       case 1: return LINK_WIDTH_1_LANE;
+       case 2: return LINK_WIDTH_2_LANES;
+       case 4: return LINK_WIDTH_4_LANES;
+       case 8: return LINK_WIDTH_8_LANES;
+       default: return 0;
+       }
+}
+
+static u32 pcie_sifive_get_link_lanes_mask(struct pcie_sifive *sv, int lanes)
+{
+       switch (lanes) {
+       case 1: return LINK_MODE_1_LANE;
+       case 2: return LINK_MODE_2_LANES;
+       case 4: return LINK_MODE_4_LANES;
+       case 8: return LINK_MODE_8_LANES;
+       default: return 0;
+       }
+}
+
+static void pcie_sifive_set_link_num_lanes(struct pcie_sifive *sv, int lanes)
+{
+       u32 mode;
+
+       mode = pcie_sifive_get_link_lanes_mask(sv, lanes);
+       if (mode) {
+               u32 val;
+
+               val = readl(sv->ctrl.iobase + LINK_CONTROL);
+               val &= ~LINK_MODE_MASK;
+               val |= mode;
+               writel(val, sv->ctrl.iobase + LINK_CONTROL);
+       }
+}
+
+static void pcie_sifive_set_link_width(struct pcie_sifive *sv, int lanes)
+{
+       u32 lwidth;
+
+       lwidth = pcie_sifive_get_link_width_mask(sv, lanes);
+       if (lwidth) {
+               u32 val;
+
+               val = readl(sv->ctrl.iobase + LINK_WIDTH_SPEED_CONTROL);
+               val &= ~LINK_WIDTH_MASK;
+               val |= lwidth;
+               writel(val, sv->ctrl.iobase + LINK_WIDTH_SPEED_CONTROL);
+       }
+}
+
+static int pcie_sifive_check_link(struct pcie_sifive *sv)
+{
+       u32 val;
+
+       val = readl(sv->ctrl.iobase + PHY_DEBUG_R1);
+       return (val & PHY_DEBUG_R1_LINK_UP) &&
+               !(val & PHY_DEBUG_R1_LINK_IN_TRAINING);
+}
+
+static void pcie_sifive_force_gen1(struct pcie_sifive *sv)
+{
+       u32 val, linkcap;
+
+       /*
+        * Force Gen1 operation when starting the link. In case the link is
+        * started in Gen2 mode, there is a possibility the devices on the
+        * bus will not be detected at all. This happens with PCIe switches.
+        */
+
+       /* ctrl_ro_wr_enable */
+       val = readl(sv->ctrl.iobase + PCIE_MISC_CONTROL_1);
+       val |= DBI_RO_WR_EN;
+       writel(val, sv->ctrl.iobase + PCIE_MISC_CONTROL_1);
+
+       /* configure link cap */
+       linkcap = readl(sv->ctrl.iobase + PF0_PCIE_CAP_LINK_CAP);
+       linkcap |= PCIE_LINK_CAP_MAX_SPEED_MASK;
+       writel(linkcap, sv->ctrl.iobase + PF0_PCIE_CAP_LINK_CAP);
+
+       /* ctrl_ro_wr_disable */
+       val &= ~DBI_RO_WR_EN;
+       writel(val, sv->ctrl.iobase + PCIE_MISC_CONTROL_1);
+}
+
+static void pcie_sifive_print_phy_debug(struct pcie_sifive *sv)
+{
+       sv_err(sv, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n",
+              readl(sv->ctrl.iobase + PHY_DEBUG_R0),
+              readl(sv->ctrl.iobase + PHY_DEBUG_R1));
+}
+
+static int pcie_sifive_set_check_atu(struct pcie_sifive *sv, u32 region)
+{
+       u32 retries, val;
+
+       /*
+        * Make sure ATU enable takes effect before any subsequent config
+        * and I/O accesses.
+        */
+       for (retries = 0; retries < ATU_WAIT_MAX_RETRIES; retries++) {
+               val = readl(sv->ctrl.iobase + region + ATU_REGION_CTRL2);
+               if (val & ATU_ENABLE)
+                       return 0;
+
+               mdelay(ATU_WAIT);
+       }
+
+       return -EBUSY;
+}
+
+static void pcie_sifive_set_atu(struct pcie_sifive *sv, u32 region,
+                               u32 ctrl1, u32 ctrl2, u64 size,
+                               u64 base_addr, u64 target_addr)
+{
+       u64 limit_addr = base_addr + size - 1;
+
+       if (upper_32_bits(size))
+               ctrl1 |= ATU_INCREASE_REGION_SIZE;
+
+       writel(lower_32_bits(base_addr),
+              sv->ctrl.iobase + region + ATU_LOWER_BASE);
+       writel(upper_32_bits(base_addr),
+              sv->ctrl.iobase + region + ATU_UPPER_BASE);
+       writel(lower_32_bits(limit_addr),
+              sv->ctrl.iobase + region + ATU_LOWER_LIMIT);
+       writel(upper_32_bits(limit_addr),
+              sv->ctrl.iobase + region + ATU_UPPER_LIMIT);
+       writel(lower_32_bits(target_addr),
+              sv->ctrl.iobase + region + ATU_LOWER_TARGET);
+       writel(upper_32_bits(target_addr),
+              sv->ctrl.iobase + region + ATU_UPPER_TARGET);
+       writel(ctrl1, sv->ctrl.iobase + region + ATU_REGION_CTRL1);
+       writel(ctrl2 | ATU_ENABLE, sv->ctrl.iobase + region + ATU_REGION_CTRL2);
+}
+
+static void pcie_sifive_set_outbound_atu(struct pcie_sifive *sv,
+                                        u32 index, u32 ctrl1, u32 ctrl2,
+                                        u64 size, u64 cpu_addr, u64 pci_addr)
+{
+       u32 region = ATU_CONFIG(ATU_OUTBOUND_REGION(index));
+
+       pcie_sifive_set_atu(sv, region, ctrl1, ctrl2, size, cpu_addr, pci_addr);
+
+       if (pcie_sifive_set_check_atu(sv, region))
+               sv_err(sv, "Outbound ATU could not be enabled\n");
+}
+
+static void __iomem *pcie_sifive_cfg_outbound_atu(struct pcie_sifive *sv,
+                                                 u32 bdf, u32 where)
+{
+       u32 bus, ctrl1;
+
+       where &= ~0x3;
+
+       bus = ((bdf >> 16) & 0xff) - sv->pci.pp.root_bus_nr;
+       if (!bus)
+               return sv->ctrl.iobase + PCI_CONFIG(where);
+
+       if (bus == 1)
+               ctrl1 = ATU_TYPE_CFG0;
+       else
+               ctrl1 = ATU_TYPE_CFG1;
+
+       bdf = (bus << 16) | (bdf & 0xffff);
+       pcie_sifive_set_outbound_atu(sv, 1, ctrl1, 0, SZ_4K,
+                                    sv->pci.pp.cfg1_base,
+                                    (u64)(bdf << 8));
+
+       return sv->pci.pp.va_cfg1_base + where;
+}
+
+static void pcie_sifive_set_outbound_mem_atu(struct pcie_sifive *sv, u32 index,
+                                            u64 size, u64 cpu_addr,
+                                            u64 pci_addr)
+{
+       pcie_sifive_set_outbound_atu(sv, index, ATU_TYPE_MEM, 0,
+                                    size, cpu_addr, pci_addr);
+}
+
+static void pcie_sifive_set_outbound_io_atu(struct pcie_sifive *sv, u32 index,
+                                           u64 size, u64 cpu_addr,
+                                           u64 pci_addr)
+{
+       pcie_sifive_set_outbound_atu(sv, index, ATU_TYPE_IO, 0,
+                                    size, cpu_addr, pci_addr);
+}
+
+static int pcie_sifive_set_outbound_ecam_atu(struct pcie_sifive *sv, u32 index,
+                                            u64 cpu_addr)
+{
+       pcie_sifive_set_outbound_atu(sv, index++,
+                                    ATU_TYPE_CFG0, ATU_CFG_SHIFT_MODE,
+                                    SZ_4K, cpu_addr, 0);
+
+       pcie_sifive_set_outbound_atu(sv, index++,
+                                    ATU_TYPE_CFG1, ATU_CFG_SHIFT_MODE,
+                                    SZ_256M - SZ_1M,
+                                    cpu_addr + SZ_1M,
+                                    SZ_1M);
+       return index;
+}
+
+static void pcie_sifive_assert_phy_reset(struct pcie_sifive *sv)
+{
+       writel(0x1, sv->priv.iobase + PCIEX8MGMT_APP_HOLD_PHY_RST);
+}
+
+static int pcie_sifive_wait_for_link(struct pcie_sifive *sv)
+{
+       u32 val;
+       int timeout;
+
+       /* Wait for the link to train */
+       mdelay(20);
+       timeout = 20;
+
+       do {
+               mdelay(1);
+       } while (--timeout && !pcie_sifive_check_link(sv));
+
+       val = readl(sv->ctrl.iobase + PHY_DEBUG_R1);
+       if (!(val & PHY_DEBUG_R1_LINK_UP) ||
+           (val & PHY_DEBUG_R1_LINK_IN_TRAINING)) {
+               sv_info(sv, "Failed to negotiate PCIe link!\n");
+               pcie_sifive_print_phy_debug(sv);
+               pcie_sifive_assert_phy_reset(sv);
+               return -ETIMEDOUT;
+       }
+
+       sv_info(sv, "PCIe Link up, Gen%i\n",
+               readw(sv->ctrl.iobase + PF0_PCIE_CAP_LINK_STATUS) &
+               PCIE_LINK_STATUS_SPEED_MASK);
+
+       return 0;
+}
+
+static void pcie_sifive_setup_link(struct pcie_sifive *sv)
+{
+       u32 lanes;
+
+       lanes = pcie_sifive_get_required_property(sv, "num-lanes");
+       if (lanes > 0) {
+               pcie_sifive_set_link_num_lanes(sv, lanes);
+               pcie_sifive_set_link_width(sv, lanes);
+       }
+}
+
+static int pcie_sifive_start_link(struct pcie_sifive *sv)
+{
+       if (pcie_sifive_check_link(sv))
+               return -EALREADY;
+
+       pcie_sifive_force_gen1(sv);
+
+       /* set ltssm */
+       pcie_sifive_priv_set_state(sv, MGMT_MISC_LTSSM_ENABLE_OFFSET,
+                                  MGMT_MISC_LTSSM_ENABLE_BIT, 1);
+       return 0;
+}
+
+static void pcie_sifive_setup_host_atu(struct pcie_sifive *sv)
+{
+       pcie_sifive_set_outbound_mem_atu(sv, 0,
+                                        sv->pci.pp.mem_size,
+                                        sv->pci.pp.mem_base,
+                                        sv->pci.pp.mem_bus_addr);
+
+       if (sv->pci.num_viewport > 2)
+               pcie_sifive_set_outbound_io_atu(sv, 2,
+                                               sv->pci.pp.io_size,
+                                               sv->pci.pp.io_base,
+                                               sv->pci.pp.io_bus_addr);
+
+       if (sv->pci.pp.ecam.iobase)
+               pcie_sifive_set_outbound_ecam_atu(sv, 3,
+                                                 (u64)sv->pci.pp.ecam.iobase);
+}
+
+static void pcie_sifive_setup_host_prefetch(struct pcie_sifive *sv)
+{
+       u32 val;
+
+       /* ctrl_ro_wr_enable */
+       val = readl(sv->ctrl.iobase + PCIE_MISC_CONTROL_1);
+       val |= DBI_RO_WR_EN;
+       writel(val, sv->ctrl.iobase + PCIE_MISC_CONTROL_1);
+
+       writew(0xf, sv->ctrl.iobase + PCI_CONFIG(PCI_PREF_MEMORY_BASE));
+       writew(0xf, sv->ctrl.iobase + PCI_CONFIG(PCI_PREF_MEMORY_LIMIT));
+       writel(0x20, sv->ctrl.iobase + PCI_CONFIG(PCI_PREF_BASE_UPPER32));
+       writel(0x40, sv->ctrl.iobase + PCI_CONFIG(PCI_PREF_LIMIT_UPPER32));
+
+       /* ctrl_ro_wr_disable */
+       val &= ~DBI_RO_WR_EN;
+       writel(val, sv->ctrl.iobase + PCIE_MISC_CONTROL_1);
+}
+
+static void pcie_sifive_setup_host(struct pcie_sifive *sv)
+{
+       u32 val;
+
+       sv->pci.iatu_unroll_enabled = true;
+
+       pcie_sifive_setup_link(sv);
+
+       /* ctrl_ro_wr_enable */
+       val = readl(sv->ctrl.iobase + PCIE_MISC_CONTROL_1);
+       val |= DBI_RO_WR_EN;
+       writel(val, sv->ctrl.iobase + PCIE_MISC_CONTROL_1);
+
+       /* Setup correct class code for host bridge */
+       writew(PCI_CLASS_BRIDGE_PCI,
+              sv->ctrl.iobase + PCI_CONFIG(PCI_CLASS_DEVICE));
+
+       /* ctrl_ro_wr_disable */
+       val &= ~DBI_RO_WR_EN;
+       writel(val, sv->ctrl.iobase + PCIE_MISC_CONTROL_1);
+
+       writeb(PCI_HEADER_TYPE_BRIDGE,
+              sv->ctrl.iobase + PCI_CONFIG(PCI_HEADER_TYPE));
+
+       /* Setup RC BARs */
+       writel(0x4, sv->ctrl.iobase + PCI_CONFIG(PCI_BASE_ADDRESS_0));
+       writel(0x0, sv->ctrl.iobase + PCI_CONFIG(PCI_BASE_ADDRESS_1));
+
+       pcie_sifive_set_pci_int_pin(sv, PCI_INTERRUPT_INTA);
+
+       /* Setup bus numbers */
+       val = readl(sv->ctrl.iobase + PCI_CONFIG(PCI_PRIMARY_BUS)) &
+             ~0x00ffffff;
+       val |= 0x00ff0100;
+       writel(val, sv->ctrl.iobase + PCI_CONFIG(PCI_PRIMARY_BUS));
+
+       /* Setup command register */
+       writew(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+              PCI_COMMAND_SERR, sv->ctrl.iobase + PCI_CONFIG(PCI_COMMAND));
+
+       pcie_sifive_setup_host_atu(sv);
+
+       writel(0x0, sv->ctrl.iobase + PCI_CONFIG(PCI_BASE_ADDRESS_0));
+
+       pcie_sifive_setup_host_prefetch(sv);
+}
+
+static int pcie_sifive_init_host(struct pcie_sifive *sv)
+{
+       pcie_sifive_setup_host(sv);
+
+       if (pcie_sifive_start_link(sv) == -EALREADY)
+               sv_info(sv, "PCIe link is already up\n");
+       else if (pcie_sifive_wait_for_link(sv) == -ETIMEDOUT)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int pcie_sifive_addr_valid(struct pcie_sifive *sv, pci_dev_t bdf)
+{
+       if ((PCI_BUS(bdf) == sv->pci.pp.root_bus_nr) && (PCI_DEV(bdf) > 0))
+               return 0;
+       if ((PCI_BUS(bdf) == sv->pci.pp.root_bus_nr + 1) && (PCI_DEV(bdf) > 0))
+               return 0;
+
+       return 1;
+}
+
+static int pcie_sifive_read_config(const struct udevice *bus, pci_dev_t bdf,
+                                  uint offset, ulong *valuep,
+                                  enum pci_size_t size)
+{
+       struct pcie_sifive *sv = dev_get_priv(bus);
+       void __iomem *va;
+       ulong value;
+
+       sv_debug(sv, "PCIe CFG read:  (b,d,f)=(%2d,%2d,%2d) ",
+                PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));
+
+       if (!pcie_sifive_addr_valid(sv, bdf)) {
+               sv_debug(sv, "- out of range\n");
+               *valuep = pci_get_ff(size);
+               return 0;
+       }
+
+       va = pcie_sifive_cfg_outbound_atu(sv, bdf, offset);
+       value = readl(va);
+
+       sv_debug(sv, "(addr,val)=(0x%04x, 0x%08lx)\n", offset, value);
+       *valuep = pci_conv_32_to_size(value, offset, size);
+
+       if (sv->pci.num_viewport <= 2)
+               pcie_sifive_set_outbound_io_atu(sv, 1,
+                                               sv->pci.pp.io_size,
+                                               sv->pci.pp.io_base,
+                                               sv->pci.pp.io_bus_addr);
+       return 0;
+}
+
+static int pcie_sifive_write_config(struct udevice *bus, pci_dev_t bdf,
+                                   uint offset, ulong value,
+                                   enum pci_size_t size)
+{
+       struct pcie_sifive *sv = dev_get_priv(bus);
+       void __iomem *va;
+       ulong old;
+
+       sv_debug(sv, "PCIe CFG write: (b,d,f)=(%2d,%2d,%2d) ",
+                PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));
+       sv_debug(sv, "(addr,val)=(0x%04x, 0x%08lx)\n", offset, value);
+
+       if (!pcie_sifive_addr_valid(sv, bdf)) {
+               sv_debug(sv, "- out of range\n");
+               return 0;
+       }
+
+       va = pcie_sifive_cfg_outbound_atu(sv, bdf, offset);
+       old = readl(va);
+       value = pci_conv_size_to_32(old, value, offset, size);
+       writel(value, va);
+
+       if (sv->pci.num_viewport <= 2)
+               pcie_sifive_set_outbound_io_atu(sv, 1,
+                                               sv->pci.pp.io_size,
+                                               sv->pci.pp.io_base,
+                                               sv->pci.pp.io_bus_addr);
+       return 0;
+}
+
+bool pcie_sifive_set_mode(struct pcie_sifive *sv, enum pcie_sifive_devtype 
mode)
+{
+       int ret;
+
+       pcie_sifive_assert_perstn(sv);
+       pcie_sifive_power_on(sv);
+       pcie_sifive_deassert_perstn(sv);
+
+       clk_enable(&sv->aux_ck);
+
+       /*
+        * assert hold_phy_rst (hold the controller LTSSM in reset
+        * after power_up_rst_n for register programming with cr_para)
+        */
+       pcie_sifive_assert_phy_reset(sv);
+
+       /* deassert power_up_rst_n */
+       ret = reset_deassert(&sv->reset);
+       if (ret < 0) {
+               pr_err("reset_assert() failed: %d", ret);
+               return false;
+       }
+
+       pcie_sifive_init_phy(sv);
+
+       clk_disable(&sv->aux_ck);
+
+       /* deassert phy reset */
+       writel(0x0, sv->priv.iobase + PCIEX8MGMT_APP_HOLD_PHY_RST);
+
+       /* enable pcieauxclk */
+       clk_enable(&sv->aux_ck);
+
+       /* Set desired mode while core is not operational */
+       if (mode == SV_PCIE_HOST_TYPE)
+               writel(MGMT_MISC_DEVICE_TYPE_RC,
+                      sv->priv.iobase + MGMT_MISC_DEVICE_TYPE_OFFSET);
+       else
+               writel(MGMT_MISC_DEVICE_TYPE_EP,
+                      sv->priv.iobase + MGMT_MISC_DEVICE_TYPE_OFFSET);
+
+       /* Confirm desired mode from operational core */
+       if (pcie_sifive_get_devtype(sv) != mode)
+               return false;
+
+       sv->mode = mode;
+
+       return true;
+}
+
+static int pcie_sifive_probe(struct udevice *dev)
+{
+       struct pcie_sifive *sv = dev_get_priv(dev);
+       struct udevice *parent = pci_get_controller(dev);
+       struct pci_controller *hose = dev_get_uclass_priv(parent);
+       int err;
+
+       sv->ctrl.iobase = (void __iomem *)sv->ctrl.phys_base;
+       sv->priv.iobase = (void __iomem *)sv->priv.phys_base;
+
+       sv->pci.dev = dev;
+       sv->pci.pp.root_bus_nr = dev_seq(dev);
+
+       sv->pci.pp.io_size = hose->regions[0].size;
+       sv->pci.pp.io_base = hose->regions[0].phys_start;
+       sv->pci.pp.io_bus_addr = hose->regions[0].bus_start;
+
+       sv->pci.pp.mem_size = hose->regions[1].size;
+       sv->pci.pp.mem_base = hose->regions[1].phys_start;
+       sv->pci.pp.mem_bus_addr = hose->regions[1].bus_start;
+
+       sv->pci.pp.config.iobase = (void __iomem *)sv->pci.pp.config.phys_base;
+       sv->pci.pp.ecam.iobase = (void __iomem *)sv->pci.pp.ecam.phys_base;
+
+       sv->pci.pp.cfg0_base = sv->pci.pp.config.phys_base;
+       sv->pci.pp.va_cfg0_base = (void __iomem *)sv->pci.pp.cfg0_base;
+       sv->pci.pp.cfg0_size = SZ_4K;
+
+       sv->pci.pp.cfg1_base = sv->pci.pp.cfg0_base + sv->pci.pp.cfg0_size;
+       sv->pci.pp.va_cfg1_base = (void __iomem *)sv->pci.pp.cfg1_base;
+       sv->pci.pp.cfg1_size = SZ_4K;
+
+       sv->pci.pp.msi_data = sv->pci.pp.cfg1_base + sv->pci.pp.cfg1_size;
+
+       gpio_request_by_name(dev, "pwren-gpios", 0, &sv->pwren_gpio,
+                            GPIOD_IS_OUT);
+
+       if (!dm_gpio_is_valid(&sv->pwren_gpio)) {
+               sv_info(sv, "pwren_gpio is invalid\n");
+               return -EINVAL;
+       }
+
+       gpio_request_by_name(dev, "perstn-gpios", 0, &sv->perstn_gpio,
+                            GPIOD_IS_OUT);
+
+       if (!dm_gpio_is_valid(&sv->perstn_gpio)) {
+               sv_info(sv, "perstn_gpio is invalid\n");
+               return -EINVAL;
+       }
+
+       err = clk_get_by_index(dev, 0, &sv->aux_ck);
+       if (err) {
+               sv_info(sv, "clk_get_by_index(aux_ck) failed: %d\n", err);
+               return err;
+       }
+
+       err = reset_get_by_index(dev, 0, &sv->reset);
+       if (err) {
+               sv_info(sv, "reset_get_by_index(reset) failed: %d\n", err);
+               return err;
+       }
+
+       if (!pcie_sifive_set_mode(sv, SV_PCIE_HOST_TYPE)) {
+               sv_info(sv, "Unable to set desired PCIe operation mode\n");
+               return -EINVAL;
+       }
+
+       return pcie_sifive_init_host(sv);
+}
+
+static int pcie_sifive_of_to_plat(struct udevice *dev)
+{
+       struct pcie_sifive *sv = dev_get_priv(dev);
+
+       sv->pci.dev = dev;
+
+       sv->ctrl.phys_base = dev_read_addr_size_name(dev, "dbi",
+                                                    &sv->ctrl.iosize);
+       if (sv->ctrl.phys_base == FDT_ADDR_T_NONE)
+               return -EINVAL;
+
+       sv->priv.phys_base = dev_read_addr_size_name(dev, "mgmt",
+                                                    &sv->priv.iosize);
+       if (sv->priv.phys_base == FDT_ADDR_T_NONE)
+               return -EINVAL;
+
+       sv->pci.pp.config.phys_base =
+               dev_read_addr_size_name(dev, "config",
+                                       &sv->pci.pp.config.iosize);
+       if (sv->pci.pp.config.phys_base == FDT_ADDR_T_NONE)
+               return -EINVAL;
+
+       sv->pci.pp.ecam.phys_base =
+               dev_read_addr_size_name(dev, "ecam", &sv->pci.pp.ecam.iosize);
+       if (sv->pci.pp.config.phys_base == FDT_ADDR_T_NONE)
+               sv->pci.pp.ecam.phys_base = 0;
+
+       sv->pci.num_viewport = pcie_sifive_get_property(sv, "num-viewport");
+       if (sv->pci.num_viewport == 0)
+               sv->pci.num_viewport = 2;
+
+       return 0;
+}
+
+static const struct dm_pci_ops pcie_sifive_ops = {
+       .read_config    = pcie_sifive_read_config,
+       .write_config   = pcie_sifive_write_config
+};
+
+static const struct udevice_id pcie_sifive_ids[] = {
+       { .compatible = "sifive,fu740-pcie" },
+       { .compatible = "sifive,fu740-pcie-ecam" },
+       {}
+};
+
+U_BOOT_DRIVER(pcie_sifive) = {
+       .name           = "pcie_sifive",
+       .id             = UCLASS_PCI,
+       .of_match       = pcie_sifive_ids,
+       .ops            = &pcie_sifive_ops,
+       .of_to_plat     = pcie_sifive_of_to_plat,
+       .probe          = pcie_sifive_probe,
+       .priv_auto      = sizeof(struct pcie_sifive),
+};
diff --git a/drivers/pci/pcie_sifive.h b/drivers/pci/pcie_sifive.h
new file mode 100644
index 0000000000..d12c1f715e
--- /dev/null
+++ b/drivers/pci/pcie_sifive.h
@@ -0,0 +1,374 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * SiFive FU740 DesignWare PCIe Controller
+ *
+ * Copyright (C) 2020-2021 SiFive, Inc.
+ *
+ * Based in early part on the i.MX6 PCIe host controller shim which is:
+ *
+ * Copyright (C) 2013 Kosagi
+ *             http://www.kosagi.com
+ *
+ * Based on driver from author: Alan Mikhak <amik...@wirelessfabric.com>
+ */
+
+#ifndef __PCIE_SIFIVE_H__
+#define __PCIE_SIFIVE_H__
+
+#include <clk.h>
+#include <pci.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/log2.h>
+#include <pci_ep.h>
+#include <pci_ids.h>
+#include <generic-phy.h>
+#include <regmap.h>
+#include <reset.h>
+#include <syscon.h>
+#include <asm-generic/gpio.h>
+
+#define MAX_MSI_IRQS           256
+#define MAX_MSI_IRQS_PER_CTRL  32
+#define MAX_MSI_CTRLS          (MAX_MSI_IRQS / MAX_MSI_IRQS_PER_CTRL)
+
+enum pcie_sifive_devtype {
+       SV_PCIE_UNKNOWN_TYPE = 0,
+       SV_PCIE_ENDPOINT_TYPE = 1,
+       SV_PCIE_HOST_TYPE = 3
+};
+
+struct sv_iomem {
+       fdt_size_t iosize;
+       void __iomem *iobase;
+       fdt_addr_t phys_base;
+};
+
+struct pcie_sifive {
+       struct {
+               struct udevice *dev;
+               u32 num_viewport;
+               u8 iatu_unroll_enabled;
+               struct {
+                       u8 root_bus_nr;
+                       u64 cfg0_base;
+                       void __iomem *va_cfg0_base;
+                       fdt_size_t cfg0_size;
+                       u64 cfg1_base;
+                       void __iomem *va_cfg1_base;
+                       fdt_size_t cfg1_size;
+                       u32 io_size;
+                       u64 io_base;
+                       u64 io_bus_addr;
+                       u64 mem_size;
+                       u64 mem_base;
+                       u64 mem_bus_addr;
+                       u64 msi_data;
+                       u32 num_vectors;
+                       u32 irq_mask[MAX_MSI_CTRLS];
+                       struct sv_iomem config;
+                       struct sv_iomem ecam;
+               } pp;
+       } pci;
+       struct sv_iomem ctrl;
+       struct sv_iomem priv;
+       enum pcie_sifive_devtype mode;
+       int sys_int_pin;
+       struct gpio_desc pwren_gpio;
+       struct gpio_desc perstn_gpio;
+       struct clk aux_ck;
+       struct reset_ctl reset;
+};
+
+#define sv_info(sv, fmt, arg...)       printf(fmt, ## arg)
+#define sv_warn(sv, fmt, arg...)       printf(fmt, ## arg)
+#define sv_debug(sv, fmt, arg...)      debug(fmt, ## arg)
+#define sv_err(sv, fmt, arg...)                printf(fmt, ## arg)
+
+#define pci_epf_header pci_ep_header
+
+#define VENDOR_ID_MASK GENMASK(15, 0)
+#define DEVICE_ID_SHIFT        16
+
+#ifndef PCI_MSIX_FLAGS
+#define PCI_MSIX_FLAGS         2       /* Message Control */
+#define  PCI_MSIX_FLAGS_QSIZE          0x07FF  /* Table size */
+#define  PCI_MSIX_FLAGS_MASKALL                0x4000  /* Mask all vectors */
+#define  PCI_MSIX_FLAGS_ENABLE         0x8000  /* MSI-X enable */
+#endif
+
+#ifndef PCI_REBAR_CAP
+#define PCI_REBAR_CAP          4       /* capability register */
+#define  PCI_REBAR_CAP_SIZES           0x00FFFFF0  /* supported BAR sizes */
+#endif
+
+#ifndef PCI_REBAR_CTRL
+#define PCI_REBAR_CTRL         8       /* control register */
+#define  PCI_REBAR_CTRL_BAR_IDX                0x00000007  /* BAR index */
+#define  PCI_REBAR_CTRL_NBAR_MASK      0x000000E0  /* # of resizable BARs */
+#define  PCI_REBAR_CTRL_NBAR_SHIFT     5           /* shift for # of BARs */
+#define  PCI_REBAR_CTRL_BAR_SIZE       0x00001F00  /* BAR size */
+#define  PCI_REBAR_CTRL_BAR_SHIFT      8           /* shift for BAR size */
+#endif
+
+#define MGMT_MISC_LTSSM_ENABLE_OFFSET  0x10
+#define MGMT_MISC_SYS_INT_OFFSET       0x238
+#define MGMT_MISC_EDMA_XFER_PEND_OFFSET        0x4d0
+#define MGMT_MISC_EDMA_INT_OFFSET      0x630
+#define MGMT_MISC_DEVICE_TYPE_OFFSET   0x708
+
+#define MGMT_MISC_LTSSM_ENABLE_BIT     BIT(0)
+#define MGMT_MISC_EDMA_XFER_PEND_BIT   BIT(0)
+#define MGMT_MISC_EDMA_INT_BITS                (BIT(1) | BIT(0))
+
+#define MGMT_MISC_DEVICE_TYPE_EP       0x0
+#define MGMT_MISC_DEVICE_TYPE_RC       0x4
+
+/* Doorbell Interface */
+#define DBI_OFFSET                     0x0
+#define DBI_SIZE                       0x1000
+
+/* Doorbell Interface 2 */
+#define DBI2_OFFSET                    0x100000
+#define DBI2_SIZE                      0x80
+
+/* Address Translation Units */
+#define ATU_OFFSET                     0x300000
+#define ATU_SIZE                       0x80000
+
+/* DMA Engines */
+#define DMA_OFFSET                     0x380000
+#define DMA_SIZE                       0x80000
+
+#define DMA_WRITE_ENGINE_EN_OFFSET     0x0C
+
+#define DMA_WRITE_DOORBELL_OFFSET      0x10
+
+#define DMA_READ_ENGINE_EN_OFFSET      0x2C
+
+#define DMA_READ_DOORBELL_OFFSET       0x30
+
+#define DMA_WRITE_INT_STATUS_OFFSET    0x4C
+#define DMA_WRITE_INT_MASK_OFFSET      0x54
+#define DMA_WRITE_INT_CLEAR_OFFSET     0x58
+
+#define DMA_READ_INT_STATUS_OFFSET     0xA0
+#define DMA_READ_INT_MASK_OFFSET       0xA8
+#define DMA_READ_INT_CLEAR_OFFSET      0xAC
+
+#define DMA_WRITE_LL_ERR_EN_OFFSET     0x90
+
+#define DMA_READ_LL_ERR_EN_OFFSET      0xC4
+
+#define DMA_WRITE_CONTROL1_OFFSET      0x200
+#define DMA_WRITE_TRANSFER_SIZE_OFFSET 0x208
+#define DMA_WRITE_SAR_LOW_OFFSET       0x20C
+#define DMA_WRITE_SAR_HI_OFFSET                0x210
+#define DMA_WRITE_DAR_LOW_OFFSET       0x214
+#define DMA_WRITE_DAR_HI_OFFSET                0x218
+
+#define DMA_READ_CONTROL1_OFFSET       0x300
+#define DMA_READ_TRANSFER_SIZE_OFFSET  0x308
+#define DMA_READ_SAR_LOW_OFFSET                0x30C
+#define DMA_READ_SAR_HI_OFFSET         0x310
+#define DMA_READ_DAR_LOW_OFFSET                0x314
+#define DMA_READ_DAR_HI_OFFSET         0x318
+
+#define DMA_CHAN_BIT(chan)             (BIT(chan))
+#define DMA_ENABLE_BIT_CHAN(chan)      DMA_CHAN_BIT(chan)
+#define DMA_LLLAIE_BIT_CHAN(chan)      DMA_CHAN_BIT(chan)
+#define DMA_INT_DONE_BIT_CHAN(chan)    DMA_CHAN_BIT(chan)
+#define DMA_INT_ABORT_BIT_CHAN(chan)   (BIT((chan) + 16))
+
+/* PCIe Port Logic registers (memory-mapped) */
+#define PL_OFFSET                      0x700
+#define PCIE_PL_PFLR                   (PL_OFFSET + 0x08)
+#define PCIE_PL_PFLR_LINK_STATE                (0x3f << 16)
+#define PCIE_PL_PFLR_FORCE_LINK                BIT(15)
+
+#define LINK_CONTROL                   (PL_OFFSET + 0x10)
+#define LINK_MODE(n)                   ((n) << 16)
+#define LINK_MODE_MASK                 LINK_MODE(0x3f)
+#define LINK_MODE_1_LANE               LINK_MODE(0x1)
+#define LINK_MODE_2_LANES              LINK_MODE(0x3)
+#define LINK_MODE_4_LANES              LINK_MODE(0x7)
+#define LINK_MODE_8_LANES              LINK_MODE(0xf)
+
+#define PHY_DEBUG_R0                   (PL_OFFSET + 0x28)
+
+#define PHY_DEBUG_R1                   (PL_OFFSET + 0x2c)
+#define PHY_DEBUG_R1_LINK_UP           (0x1 << 4)
+#define PHY_DEBUG_R1_LINK_IN_TRAINING  (0x1 << 29)
+
+#define LINK_WIDTH_SPEED_CONTROL       (PL_OFFSET + 0x10c)
+#define LINK_WIDTH(n)                  ((n) << 8)
+#define LINK_WIDTH_MASK                        LINK_WIDTH(0x1f)
+#define LINK_WIDTH_1_LANE              LINK_WIDTH(0x1)
+#define LINK_WIDTH_2_LANES             LINK_WIDTH(0x2)
+#define LINK_WIDTH_4_LANES             LINK_WIDTH(0x4)
+#define LINK_WIDTH_8_LANES             LINK_WIDTH(0x8)
+
+#define PHY_STAT                       (PL_OFFSET + 0x110)
+#define PHY_STAT_ACK_LOC               16
+
+#define PHY_CTRL                       (PL_OFFSET + 0x114)
+#define PHY_CTRL_DATA_LOC              0
+#define PHY_CTRL_CAP_ADR_LOC           16
+#define PHY_CTRL_CAP_DAT_LOC           17
+#define PHY_CTRL_WR_LOC                        18
+#define PHY_CTRL_RD_LOC                        19
+
+#define MSI_CTRL_BLOCK_SIZE            12
+#define MSI_CTRL_BLOCK(ctrl)           ((ctrl) * MSI_CTRL_BLOCK_SIZE)
+#define MSI_CTRL_INT_ENABLE(ctrl)      (0x828 + MSI_CTRL_BLOCK(ctrl))
+#define MSI_CTRL_INT_MASK(ctrl)                (0x82c + MSI_CTRL_BLOCK(ctrl))
+#define MSI_CTRL_INT_STATUS(ctrl)      (0x830 + MSI_CTRL_BLOCK(ctrl))
+
+#define PCIE_MISC_CONTROL_1            0x8bc
+#define DBI_RO_WR_EN                   BIT(0)
+
+#define ATU_VIEWPORT                   0x900
+#define ATU_REGION_MIN_SIZE            BIT(16)
+#define ATU_REGION_INBOUND             BIT(31)
+#define ATU_REGION_OUTBOUND            0
+
+#define ATU_VIEWPORT_CTRL_1            0x904
+#define ATU_TYPE_MASK                  0xf
+#define ATU_TYPE_MEM                   0x0
+#define ATU_TYPE_IO                    0x2
+#define ATU_TYPE_CFG0                  0x4
+#define ATU_TYPE_CFG1                  0x5
+#ifdef CONFIG_PCI_ALMOND_FPGA_REV8
+#define ATU_INCREASE_REGION_SIZE       0
+#else
+#define ATU_INCREASE_REGION_SIZE       BIT(13)
+#endif
+
+#define ATU_VIEWPORT_CTRL_2            0x908
+#define ATU_CFG_SHIFT_MODE             BIT(28)
+#define ATU_BAR_MATCH_MODE             BIT(30)
+#define ATU_ENABLE                     BIT(31)
+#define ATU_DISABLE                    (u32)~ATU_ENABLE
+
+#define ATU_MAX_IN                     16
+#define ATU_MAX_OUT                    16
+
+#define ATU_WAIT_MAX_RETRIES           5
+#define ATU_WAIT                       9
+
+/*
+ * iATU Unroll-specific register definitions
+ * From 4.80 core version the address translation will be made by unroll
+ */
+#define ATU_REGION_CTRL1               0x00
+#define ATU_REGION_CTRL2               0x04
+#define ATU_LOWER_BASE                 0x08
+#define ATU_UPPER_BASE                 0x0C
+#define ATU_LOWER_LIMIT                        0x10
+#define ATU_LOWER_TARGET               0x14
+#define ATU_UPPER_TARGET               0x18
+#define ATU_UPPER_LIMIT                        0x20
+
+#define ATU_OUTBOUND_REGION(region)    ((region) << 9)
+#define ATU_INBOUND_REGION(region)     (((region) << 9) | BIT(8))
+
+#define SIFIVE_PCIEAUXGATECFG 0x14
+#define SIFIVE_DEVICESRESETREG 0x28
+
+#define PCIEX8MGMT_PERST_N 0x0
+#define PCIEX8MGMT_APP_LTSSM_ENABLE    0x10
+#define PCIEX8MGMT_APP_HOLD_PHY_RST 0x18
+#define PCIEX8MGMT_DEVICE_TYPE         0x708
+#define PCIEX8MGMT_PHY0_CR_PARA_ADDR 0x860
+#define PCIEX8MGMT_PHY0_CR_PARA_RD_EN 0x870
+#define PCIEX8MGMT_PHY0_CR_PARA_RD_DATA 0x878
+#define PCIEX8MGMT_PHY0_CR_PARA_SEL 0x880
+#define PCIEX8MGMT_PHY0_CR_PARA_WR_DATA 0x888
+#define PCIEX8MGMT_PHY0_CR_PARA_WR_EN 0x890
+#define PCIEX8MGMT_PHY0_CR_PARA_ACK 0x898
+#define PCIEX8MGMT_PHY1_CR_PARA_ADDR 0x8a0
+#define PCIEX8MGMT_PHY1_CR_PARA_RD_EN 0x8b0
+#define PCIEX8MGMT_PHY1_CR_PARA_RD_DATA 0x8b8
+#define PCIEX8MGMT_PHY1_CR_PARA_SEL 0x8c0
+#define PCIEX8MGMT_PHY1_CR_PARA_WR_DATA 0x8c8
+#define PCIEX8MGMT_PHY1_CR_PARA_WR_EN 0x8d0
+#define PCIEX8MGMT_PHY1_CR_PARA_ACK 0x8d8
+
+#define PCIEX8MGMT_LANE_NUM 8
+#define PCIEX8MGMT_LANE 0x1008
+#define PCIEX8MGMT_LANE_OFF 0x100
+#define PCIEX8MGMT_TERM_MODE 0x0e21
+
+/* PCIe Port Logic registers (memory-mapped) */
+#define PL_OFFSET 0x700
+#define PCIE_PL_PFLR (PL_OFFSET + 0x08)
+#define PCIE_PL_PFLR_LINK_STATE_MASK           (0x3f << 16)
+#define PCIE_PL_PFLR_FORCE_LINK                        BIT(15)
+#define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28)
+#define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c)
+#define PCIE_PL_GEN2_CTRL_OFF (PL_OFFSET + 0x10c)
+#define PCIE_PL_DIRECTED_SPEED_CHANGE_OFF 0x20000
+
+#define PCIE_PHY_CTRL (PL_OFFSET + 0x114)
+#define PCIE_PHY_CTRL_DATA_LOC 0
+#define PCIE_PHY_CTRL_CAP_ADR_LOC 16
+#define PCIE_PHY_CTRL_CAP_DAT_LOC 17
+#define PCIE_PHY_CTRL_WR_LOC 18
+#define PCIE_PHY_CTRL_RD_LOC 19
+
+#define PCIE_PHY_STAT (PL_OFFSET + 0x110)
+#define PCIE_PHY_STAT_ACK_LOC 16
+
+#define PCIE_LINK_WIDTH_SPEED_CONTROL  0x80C
+
+/* PCIe Root Complex registers (memory-mapped) */
+#define PCIE_RC_PF0_MSI_CAP                    0x50
+#define PCI_MSI_CAP_ID_NEXT_CTRL_REG           (PCIE_RC_PF0_MSI_CAP + 0x0)
+
+#define PCIE_RC_PF0_MSIX_CAP                   0x0
+
+#define PCIE_DSP_PF0_PCIE_CAP_BASE             0x70
+#define PCIE_RC_LCR                            (PCIE_DSP_PF0_PCIE_CAP_BASE + 
0xc)
+#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1       0x1
+#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2       0x2
+#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN3       0x3
+#define PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK       0xf
+
+#define PCIE_RC_LCSR                           (PCIE_DSP_PF0_PCIE_CAP_BASE + 
0x10)
+
+#define DMA_CONFIG(r)                  (DMA_OFFSET + (r))
+#define ATU_CONFIG(r)                  (ATU_OFFSET + (r))
+#define PCI_SHADOW(r)                  (DBI2_OFFSET + (r))
+#define PCI_CONFIG(r)                  (DBI_OFFSET + (r))
+#define MSI_CAPABILITIES(r)            PCI_CONFIG(PCIE_RC_PF0_MSI_CAP + (r))
+#define MSIX_CAPABILITIES(r)           PCI_CONFIG(PCIE_RC_PF0_MSIX_CAP + (r))
+#define PCIE_CAPABILITIES(r)           PCI_CONFIG(PCIE_DSP_PF0_PCIE_CAP_BASE + 
(r))
+
+#define PF0_PCIE_CAP_LINK_CAP          PCIE_CAPABILITIES(0xc)
+#define PCIE_LINK_CAP_MAX_SPEED_MASK   0xf
+#define PCIE_LINK_CAP_MAX_SPEED_GEN1   BIT(0)
+#define PCIE_LINK_CAP_MAX_SPEED_GEN2   BIT(1)
+#define PCIE_LINK_CAP_MAX_SPEED_GEN3   BIT(2)
+#define PCIE_LINK_CAP_MAX_SPEED_GEN4   BIT(3)
+
+#define PF0_PCIE_CAP_LINK_CONTROL      PCIE_CAPABILITIES(0x10)
+
+#define PF0_PCIE_CAP_LINK_STATUS       PCIE_CAPABILITIES(0x12)
+#define PCIE_LINK_STATUS_SPEED_MASK    0xf
+
+#define PCI_CFG0_REGION_OFFSET         0x00000000
+#define PCI_CFG0_REGION_SIZE           0x00001000      /* 2^12 = 4KB */
+#define PCI_CFG1_REGION_OFFSET         0x00001000
+#define PCI_CFG1_REGION_SIZE           0x00001000      /* 2^12 = 4KB */
+#define PCI_MSI_REGION_OFFSET          0x00002000
+#define PCI_MSI_REGION_SIZE            0x00001000      /* 2^12 = 4KB */
+#define PCI_IO_REGION_OFFSET           0x00080000
+#define PCI_IO_REGION_SIZE             0x00010000      /* 2^16 = 64KB */
+#define PCI_MEM_REGION_OFFSET          0x04000000
+#define PCI_MEM_REGION_SIZE            0x04000000      /* 2^26 = 64MB */
+#define PCI_AUTOCFG_REGION_OFFSET      0x08000000
+#define PCI_AUTOCFG_REGION_SIZE                0x08000000      /* 2^27 = 128MB 
*/
+#define PCI_ECAM_REGION_OFFSET         0x10000000
+#define PCI_ECAM_REGION_SIZE           0x10000000      /* 2^28 = 256MB */
+
+#endif /* __PCIE_SIFIVE_H__ */
-- 
2.31.0

Reply via email to