PCI: tegra: Add support for PCIe endpoint mode in Tegra194
authorVidya Sagar <vidyas@nvidia.com>
Tue, 3 Mar 2020 18:10:52 +0000 (23:40 +0530)
committerLorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Tue, 31 Mar 2020 08:13:17 +0000 (09:13 +0100)
Add support for the endpoint mode of Synopsys DesignWare core based
dual mode PCIe controllers present in Tegra194 SoC.

Signed-off-by: Vidya Sagar <vidyas@nvidia.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Acked-by: Thierry Reding <treding@nvidia.com>
drivers/pci/controller/dwc/Kconfig
drivers/pci/controller/dwc/pcie-designware-ep.c
drivers/pci/controller/dwc/pcie-tegra194.c

index 0830dfc..03dcaf6 100644 (file)
@@ -248,14 +248,37 @@ config PCI_MESON
          implement the driver.
 
 config PCIE_TEGRA194
-       tristate "NVIDIA Tegra194 (and later) PCIe controller"
+       tristate
+
+config PCIE_TEGRA194_HOST
+       tristate "NVIDIA Tegra194 (and later) PCIe controller - Host Mode"
        depends on ARCH_TEGRA_194_SOC || COMPILE_TEST
        depends on PCI_MSI_IRQ_DOMAIN
        select PCIE_DW_HOST
        select PHY_TEGRA194_P2U
+       select PCIE_TEGRA194
+       help
+         Enables support for the PCIe controller in the NVIDIA Tegra194 SoC to
+         work in host mode. There are two instances of PCIe controllers in
+         Tegra194. This controller can work either as EP or RC. In order to
+         enable host-specific features PCIE_TEGRA194_HOST must be selected and
+         in order to enable device-specific features PCIE_TEGRA194_EP must be
+         selected. This uses the DesignWare core.
+
+config PCIE_TEGRA194_EP
+       tristate "NVIDIA Tegra194 (and later) PCIe controller - Endpoint Mode"
+       depends on ARCH_TEGRA_194_SOC || COMPILE_TEST
+       depends on PCI_ENDPOINT
+       select PCIE_DW_EP
+       select PHY_TEGRA194_P2U
+       select PCIE_TEGRA194
        help
-         Say Y here if you want support for DesignWare core based PCIe host
-         controller found in NVIDIA Tegra194 SoC.
+         Enables support for the PCIe controller in the NVIDIA Tegra194 SoC to
+         work in host mode. There are two instances of PCIe controllers in
+         Tegra194. This controller can work either as EP or RC. In order to
+         enable host-specific features PCIE_TEGRA194_HOST must be selected and
+         in order to enable device-specific features PCIE_TEGRA194_EP must be
+         selected. This uses the DesignWare core.
 
 config PCIE_UNIPHIER
        bool "Socionext UniPhier PCIe controllers"
index dfbb806..8e9f311 100644 (file)
@@ -18,6 +18,7 @@ void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
 
        pci_epc_linkup(epc);
 }
+EXPORT_SYMBOL_GPL(dw_pcie_ep_linkup);
 
 void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep)
 {
@@ -25,6 +26,7 @@ void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep)
 
        pci_epc_init_notify(epc);
 }
+EXPORT_SYMBOL_GPL(dw_pcie_ep_init_notify);
 
 static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar,
                                   int flags)
@@ -536,6 +538,7 @@ int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(dw_pcie_ep_init_complete);
 
 int dw_pcie_ep_init(struct dw_pcie_ep *ep)
 {
@@ -630,3 +633,4 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
 
        return dw_pcie_ep_init_complete(ep);
 }
+EXPORT_SYMBOL_GPL(dw_pcie_ep_init);
index cbe95f0..eeeca18 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/debugfs.h>
 #include <linux/delay.h>
 #include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/interrupt.h>
 #include <linux/iopoll.h>
 #include <linux/kernel.h>
@@ -53,6 +54,7 @@
 #define APPL_INTR_EN_L0_0_LINK_STATE_INT_EN    BIT(0)
 #define APPL_INTR_EN_L0_0_MSI_RCV_INT_EN       BIT(4)
 #define APPL_INTR_EN_L0_0_INT_INT_EN           BIT(8)
+#define APPL_INTR_EN_L0_0_PCI_CMD_EN_INT_EN    BIT(15)
 #define APPL_INTR_EN_L0_0_CDM_REG_CHK_INT_EN   BIT(19)
 #define APPL_INTR_EN_L0_0_SYS_INTR_EN          BIT(30)
 #define APPL_INTR_EN_L0_0_SYS_MSI_INTR_EN      BIT(31)
 #define APPL_INTR_STATUS_L0                    0xC
 #define APPL_INTR_STATUS_L0_LINK_STATE_INT     BIT(0)
 #define APPL_INTR_STATUS_L0_INT_INT            BIT(8)
+#define APPL_INTR_STATUS_L0_PCI_CMD_EN_INT     BIT(15)
+#define APPL_INTR_STATUS_L0_PEX_RST_INT                BIT(16)
 #define APPL_INTR_STATUS_L0_CDM_REG_CHK_INT    BIT(18)
 
 #define APPL_INTR_EN_L1_0_0                            0x1C
 #define APPL_INTR_EN_L1_0_0_LINK_REQ_RST_NOT_INT_EN    BIT(1)
+#define APPL_INTR_EN_L1_0_0_RDLH_LINK_UP_INT_EN                BIT(3)
+#define APPL_INTR_EN_L1_0_0_HOT_RESET_DONE_INT_EN      BIT(30)
 
 #define APPL_INTR_STATUS_L1_0_0                                0x20
 #define APPL_INTR_STATUS_L1_0_0_LINK_REQ_RST_NOT_CHGED BIT(1)
+#define APPL_INTR_STATUS_L1_0_0_RDLH_LINK_UP_CHGED     BIT(3)
+#define APPL_INTR_STATUS_L1_0_0_HOT_RESET_DONE         BIT(30)
 
 #define APPL_INTR_STATUS_L1_1                  0x2C
 #define APPL_INTR_STATUS_L1_2                  0x30
 #define APPL_INTR_STATUS_L1_3                  0x34
 #define APPL_INTR_STATUS_L1_6                  0x3C
 #define APPL_INTR_STATUS_L1_7                  0x40
+#define APPL_INTR_STATUS_L1_15_CFG_BME_CHGED   BIT(1)
 
 #define APPL_INTR_EN_L1_8_0                    0x44
 #define APPL_INTR_EN_L1_8_BW_MGT_INT_EN                BIT(2)
 #define APPL_INTR_STATUS_L1_18_CDM_REG_CHK_CMP_ERR     BIT(1)
 #define APPL_INTR_STATUS_L1_18_CDM_REG_CHK_LOGIC_ERR   BIT(0)
 
+#define APPL_MSI_CTRL_1                                0xAC
+
 #define APPL_MSI_CTRL_2                                0xB0
 
+#define APPL_LEGACY_INTX                       0xB8
+
 #define APPL_LTR_MSG_1                         0xC4
 #define LTR_MSG_REQ                            BIT(15)
 #define LTR_MST_NO_SNOOP_SHIFT                 16
 #define AMBA_ERROR_RESPONSE_CRS_OKAY_FFFFFFFF  1
 #define AMBA_ERROR_RESPONSE_CRS_OKAY_FFFF0001  2
 
+#define MSIX_ADDR_MATCH_LOW_OFF                        0x940
+#define MSIX_ADDR_MATCH_LOW_OFF_EN             BIT(0)
+#define MSIX_ADDR_MATCH_LOW_OFF_MASK           GENMASK(31, 2)
+
+#define MSIX_ADDR_MATCH_HIGH_OFF               0x944
+#define MSIX_ADDR_MATCH_HIGH_OFF_MASK          GENMASK(31, 0)
+
 #define PORT_LOGIC_MSIX_DOORBELL                       0x948
 
 #define CAP_SPCIE_CAP_OFF                      0x154
 #define GEN3_CORE_CLK_FREQ     250000000
 #define GEN4_CORE_CLK_FREQ     500000000
 
+#define LTR_MSG_TIMEOUT                (100 * 1000)
+
+#define PERST_DEBOUNCE_TIME    (5 * 1000)
+
+#define EP_STATE_DISABLED      0
+#define EP_STATE_ENABLED       1
+
 static const unsigned int pcie_gen_freq[] = {
        GEN1_CORE_CLK_FREQ,
        GEN2_CORE_CLK_FREQ,
@@ -260,6 +287,8 @@ struct tegra_pcie_dw {
        struct dw_pcie pci;
        struct tegra_bpmp *bpmp;
 
+       enum dw_pcie_device_mode mode;
+
        bool supports_clkreq;
        bool enable_cdm_check;
        bool link_state;
@@ -283,6 +312,16 @@ struct tegra_pcie_dw {
        struct phy **phys;
 
        struct dentry *debugfs;
+
+       /* Endpoint mode specific */
+       struct gpio_desc *pex_rst_gpiod;
+       struct gpio_desc *pex_refclk_sel_gpiod;
+       unsigned int pex_rst_irq;
+       int ep_state;
+};
+
+struct tegra_pcie_dw_of_data {
+       enum dw_pcie_device_mode mode;
 };
 
 static inline struct tegra_pcie_dw *to_tegra_pcie(struct dw_pcie *pci)
@@ -339,8 +378,9 @@ static void apply_bad_link_workaround(struct pcie_port *pp)
        }
 }
 
-static irqreturn_t tegra_pcie_rp_irq_handler(struct tegra_pcie_dw *pcie)
+static irqreturn_t tegra_pcie_rp_irq_handler(int irq, void *arg)
 {
+       struct tegra_pcie_dw *pcie = arg;
        struct dw_pcie *pci = &pcie->pci;
        struct pcie_port *pp = &pci->pp;
        u32 val, tmp;
@@ -411,11 +451,121 @@ static irqreturn_t tegra_pcie_rp_irq_handler(struct tegra_pcie_dw *pcie)
        return IRQ_HANDLED;
 }
 
-static irqreturn_t tegra_pcie_irq_handler(int irq, void *arg)
+static void pex_ep_event_hot_rst_done(struct tegra_pcie_dw *pcie)
+{
+       u32 val;
+
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L0);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_0_0);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_1);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_2);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_3);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_6);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_7);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_8_0);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_9);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_10);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_11);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_13);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_14);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_15);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_17);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_MSI_CTRL_2);
+
+       val = appl_readl(pcie, APPL_CTRL);
+       val |= APPL_CTRL_LTSSM_EN;
+       appl_writel(pcie, val, APPL_CTRL);
+}
+
+static irqreturn_t tegra_pcie_ep_irq_thread(int irq, void *arg)
 {
        struct tegra_pcie_dw *pcie = arg;
+       struct dw_pcie *pci = &pcie->pci;
+       u32 val, speed;
+
+       speed = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKSTA) &
+               PCI_EXP_LNKSTA_CLS;
+       clk_set_rate(pcie->core_clk, pcie_gen_freq[speed - 1]);
+
+       /* If EP doesn't advertise L1SS, just return */
+       val = dw_pcie_readl_dbi(pci, pcie->cfg_link_cap_l1sub);
+       if (!(val & (PCI_L1SS_CAP_ASPM_L1_1 | PCI_L1SS_CAP_ASPM_L1_2)))
+               return IRQ_HANDLED;
+
+       /* Check if BME is set to '1' */
+       val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
+       if (val & PCI_COMMAND_MASTER) {
+               ktime_t timeout;
+
+               /* 110us for both snoop and no-snoop */
+               val = 110 | (2 << PCI_LTR_SCALE_SHIFT) | LTR_MSG_REQ;
+               val |= (val << LTR_MST_NO_SNOOP_SHIFT);
+               appl_writel(pcie, val, APPL_LTR_MSG_1);
+
+               /* Send LTR upstream */
+               val = appl_readl(pcie, APPL_LTR_MSG_2);
+               val |= APPL_LTR_MSG_2_LTR_MSG_REQ_STATE;
+               appl_writel(pcie, val, APPL_LTR_MSG_2);
+
+               timeout = ktime_add_us(ktime_get(), LTR_MSG_TIMEOUT);
+               for (;;) {
+                       val = appl_readl(pcie, APPL_LTR_MSG_2);
+                       if (!(val & APPL_LTR_MSG_2_LTR_MSG_REQ_STATE))
+                               break;
+                       if (ktime_after(ktime_get(), timeout))
+                               break;
+                       usleep_range(1000, 1100);
+               }
+               if (val & APPL_LTR_MSG_2_LTR_MSG_REQ_STATE)
+                       dev_err(pcie->dev, "Failed to send LTR message\n");
+       }
 
-       return tegra_pcie_rp_irq_handler(pcie);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t tegra_pcie_ep_hard_irq(int irq, void *arg)
+{
+       struct tegra_pcie_dw *pcie = arg;
+       struct dw_pcie_ep *ep = &pcie->pci.ep;
+       int spurious = 1;
+       u32 val, tmp;
+
+       val = appl_readl(pcie, APPL_INTR_STATUS_L0);
+       if (val & APPL_INTR_STATUS_L0_LINK_STATE_INT) {
+               val = appl_readl(pcie, APPL_INTR_STATUS_L1_0_0);
+               appl_writel(pcie, val, APPL_INTR_STATUS_L1_0_0);
+
+               if (val & APPL_INTR_STATUS_L1_0_0_HOT_RESET_DONE)
+                       pex_ep_event_hot_rst_done(pcie);
+
+               if (val & APPL_INTR_STATUS_L1_0_0_RDLH_LINK_UP_CHGED) {
+                       tmp = appl_readl(pcie, APPL_LINK_STATUS);
+                       if (tmp & APPL_LINK_STATUS_RDLH_LINK_UP) {
+                               dev_dbg(pcie->dev, "Link is up with Host\n");
+                               dw_pcie_ep_linkup(ep);
+                       }
+               }
+
+               spurious = 0;
+       }
+
+       if (val & APPL_INTR_STATUS_L0_PCI_CMD_EN_INT) {
+               val = appl_readl(pcie, APPL_INTR_STATUS_L1_15);
+               appl_writel(pcie, val, APPL_INTR_STATUS_L1_15);
+
+               if (val & APPL_INTR_STATUS_L1_15_CFG_BME_CHGED)
+                       return IRQ_WAKE_THREAD;
+
+               spurious = 0;
+       }
+
+       if (spurious) {
+               dev_warn(pcie->dev, "Random interrupt (STATUS = 0x%08X)\n",
+                        val);
+               appl_writel(pcie, val, APPL_INTR_STATUS_L0);
+       }
+
+       return IRQ_HANDLED;
 }
 
 static int tegra_pcie_dw_rd_own_conf(struct pcie_port *pp, int where, int size,
@@ -884,8 +1034,26 @@ static void tegra_pcie_set_msi_vec_num(struct pcie_port *pp)
        pp->num_vectors = MAX_MSI_IRQS;
 }
 
+static int tegra_pcie_dw_start_link(struct dw_pcie *pci)
+{
+       struct tegra_pcie_dw *pcie = to_tegra_pcie(pci);
+
+       enable_irq(pcie->pex_rst_irq);
+
+       return 0;
+}
+
+static void tegra_pcie_dw_stop_link(struct dw_pcie *pci)
+{
+       struct tegra_pcie_dw *pcie = to_tegra_pcie(pci);
+
+       disable_irq(pcie->pex_rst_irq);
+}
+
 static const struct dw_pcie_ops tegra_dw_pcie_ops = {
        .link_up = tegra_pcie_dw_link_up,
+       .start_link = tegra_pcie_dw_start_link,
+       .stop_link = tegra_pcie_dw_stop_link,
 };
 
 static struct dw_pcie_host_ops tegra_pcie_dw_host_ops = {
@@ -986,6 +1154,26 @@ static int tegra_pcie_dw_parse_dt(struct tegra_pcie_dw *pcie)
        pcie->enable_cdm_check =
                of_property_read_bool(np, "snps,enable-cdm-check");
 
+       if (pcie->mode == DW_PCIE_RC_TYPE)
+               return 0;
+
+       /* Endpoint mode specific DT entries */
+       pcie->pex_rst_gpiod = devm_gpiod_get(pcie->dev, "reset", GPIOD_IN);
+       if (IS_ERR(pcie->pex_rst_gpiod)) {
+               dev_err(pcie->dev, "Failed to get PERST GPIO: %ld\n",
+                       PTR_ERR(pcie->pex_rst_gpiod));
+               return PTR_ERR(pcie->pex_rst_gpiod);
+       }
+
+       pcie->pex_refclk_sel_gpiod = devm_gpiod_get(pcie->dev,
+                                                   "nvidia,refclk-select",
+                                                   GPIOD_OUT_HIGH);
+       if (IS_ERR(pcie->pex_refclk_sel_gpiod)) {
+               dev_info(pcie->dev, "Failed to get REFCLK select GPIOs: %ld\n",
+                        PTR_ERR(pcie->pex_refclk_sel_gpiod));
+               pcie->pex_refclk_sel_gpiod = NULL;
+       }
+
        return 0;
 }
 
@@ -1017,6 +1205,34 @@ static int tegra_pcie_bpmp_set_ctrl_state(struct tegra_pcie_dw *pcie,
        return tegra_bpmp_transfer(pcie->bpmp, &msg);
 }
 
+static int tegra_pcie_bpmp_set_pll_state(struct tegra_pcie_dw *pcie,
+                                        bool enable)
+{
+       struct mrq_uphy_response resp;
+       struct tegra_bpmp_message msg;
+       struct mrq_uphy_request req;
+
+       memset(&req, 0, sizeof(req));
+       memset(&resp, 0, sizeof(resp));
+
+       if (enable) {
+               req.cmd = CMD_UPHY_PCIE_EP_CONTROLLER_PLL_INIT;
+               req.ep_ctrlr_pll_init.ep_controller = pcie->cid;
+       } else {
+               req.cmd = CMD_UPHY_PCIE_EP_CONTROLLER_PLL_OFF;
+               req.ep_ctrlr_pll_off.ep_controller = pcie->cid;
+       }
+
+       memset(&msg, 0, sizeof(msg));
+       msg.mrq = MRQ_UPHY;
+       msg.tx.data = &req;
+       msg.tx.size = sizeof(req);
+       msg.rx.data = &resp;
+       msg.rx.size = sizeof(resp);
+
+       return tegra_bpmp_transfer(pcie->bpmp, &msg);
+}
+
 static void tegra_pcie_downstream_dev_to_D0(struct tegra_pcie_dw *pcie)
 {
        struct pcie_port *pp = &pcie->pci.pp;
@@ -1427,8 +1643,396 @@ fail_pm_get_sync:
        return ret;
 }
 
+static void pex_ep_event_pex_rst_assert(struct tegra_pcie_dw *pcie)
+{
+       u32 val;
+       int ret;
+
+       if (pcie->ep_state == EP_STATE_DISABLED)
+               return;
+
+       /* Disable LTSSM */
+       val = appl_readl(pcie, APPL_CTRL);
+       val &= ~APPL_CTRL_LTSSM_EN;
+       appl_writel(pcie, val, APPL_CTRL);
+
+       ret = readl_poll_timeout(pcie->appl_base + APPL_DEBUG, val,
+                                ((val & APPL_DEBUG_LTSSM_STATE_MASK) >>
+                                APPL_DEBUG_LTSSM_STATE_SHIFT) ==
+                                LTSSM_STATE_PRE_DETECT,
+                                1, LTSSM_TIMEOUT);
+       if (ret)
+               dev_err(pcie->dev, "Failed to go Detect state: %d\n", ret);
+
+       reset_control_assert(pcie->core_rst);
+
+       tegra_pcie_disable_phy(pcie);
+
+       reset_control_assert(pcie->core_apb_rst);
+
+       clk_disable_unprepare(pcie->core_clk);
+
+       pm_runtime_put_sync(pcie->dev);
+
+       ret = tegra_pcie_bpmp_set_pll_state(pcie, false);
+       if (ret)
+               dev_err(pcie->dev, "Failed to turn off UPHY: %d\n", ret);
+
+       pcie->ep_state = EP_STATE_DISABLED;
+       dev_dbg(pcie->dev, "Uninitialization of endpoint is completed\n");
+}
+
+static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
+{
+       struct dw_pcie *pci = &pcie->pci;
+       struct dw_pcie_ep *ep = &pci->ep;
+       struct device *dev = pcie->dev;
+       u32 val;
+       int ret;
+
+       if (pcie->ep_state == EP_STATE_ENABLED)
+               return;
+
+       ret = pm_runtime_get_sync(dev);
+       if (ret < 0) {
+               dev_err(dev, "Failed to get runtime sync for PCIe dev: %d\n",
+                       ret);
+               return;
+       }
+
+       ret = tegra_pcie_bpmp_set_pll_state(pcie, true);
+       if (ret) {
+               dev_err(dev, "Failed to init UPHY for PCIe EP: %d\n", ret);
+               goto fail_pll_init;
+       }
+
+       ret = clk_prepare_enable(pcie->core_clk);
+       if (ret) {
+               dev_err(dev, "Failed to enable core clock: %d\n", ret);
+               goto fail_core_clk_enable;
+       }
+
+       ret = reset_control_deassert(pcie->core_apb_rst);
+       if (ret) {
+               dev_err(dev, "Failed to deassert core APB reset: %d\n", ret);
+               goto fail_core_apb_rst;
+       }
+
+       ret = tegra_pcie_enable_phy(pcie);
+       if (ret) {
+               dev_err(dev, "Failed to enable PHY: %d\n", ret);
+               goto fail_phy;
+       }
+
+       /* Clear any stale interrupt statuses */
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L0);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_0_0);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_1);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_2);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_3);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_6);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_7);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_8_0);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_9);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_10);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_11);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_13);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_14);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_15);
+       appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_17);
+
+       /* configure this core for EP mode operation */
+       val = appl_readl(pcie, APPL_DM_TYPE);
+       val &= ~APPL_DM_TYPE_MASK;
+       val |= APPL_DM_TYPE_EP;
+       appl_writel(pcie, val, APPL_DM_TYPE);
+
+       appl_writel(pcie, 0x0, APPL_CFG_SLCG_OVERRIDE);
+
+       val = appl_readl(pcie, APPL_CTRL);
+       val |= APPL_CTRL_SYS_PRE_DET_STATE;
+       val |= APPL_CTRL_HW_HOT_RST_EN;
+       appl_writel(pcie, val, APPL_CTRL);
+
+       val = appl_readl(pcie, APPL_CFG_MISC);
+       val |= APPL_CFG_MISC_SLV_EP_MODE;
+       val |= (APPL_CFG_MISC_ARCACHE_VAL << APPL_CFG_MISC_ARCACHE_SHIFT);
+       appl_writel(pcie, val, APPL_CFG_MISC);
+
+       val = appl_readl(pcie, APPL_PINMUX);
+       val |= APPL_PINMUX_CLK_OUTPUT_IN_OVERRIDE_EN;
+       val |= APPL_PINMUX_CLK_OUTPUT_IN_OVERRIDE;
+       appl_writel(pcie, val, APPL_PINMUX);
+
+       appl_writel(pcie, pcie->dbi_res->start & APPL_CFG_BASE_ADDR_MASK,
+                   APPL_CFG_BASE_ADDR);
+
+       appl_writel(pcie, pcie->atu_dma_res->start &
+                   APPL_CFG_IATU_DMA_BASE_ADDR_MASK,
+                   APPL_CFG_IATU_DMA_BASE_ADDR);
+
+       val = appl_readl(pcie, APPL_INTR_EN_L0_0);
+       val |= APPL_INTR_EN_L0_0_SYS_INTR_EN;
+       val |= APPL_INTR_EN_L0_0_LINK_STATE_INT_EN;
+       val |= APPL_INTR_EN_L0_0_PCI_CMD_EN_INT_EN;
+       appl_writel(pcie, val, APPL_INTR_EN_L0_0);
+
+       val = appl_readl(pcie, APPL_INTR_EN_L1_0_0);
+       val |= APPL_INTR_EN_L1_0_0_HOT_RESET_DONE_INT_EN;
+       val |= APPL_INTR_EN_L1_0_0_RDLH_LINK_UP_INT_EN;
+       appl_writel(pcie, val, APPL_INTR_EN_L1_0_0);
+
+       reset_control_deassert(pcie->core_rst);
+
+       if (pcie->update_fc_fixup) {
+               val = dw_pcie_readl_dbi(pci, CFG_TIMER_CTRL_MAX_FUNC_NUM_OFF);
+               val |= 0x1 << CFG_TIMER_CTRL_ACK_NAK_SHIFT;
+               dw_pcie_writel_dbi(pci, CFG_TIMER_CTRL_MAX_FUNC_NUM_OFF, val);
+       }
+
+       config_gen3_gen4_eq_presets(pcie);
+
+       init_host_aspm(pcie);
+
+       /* Disable ASPM-L1SS advertisement if there is no CLKREQ routing */
+       if (!pcie->supports_clkreq) {
+               disable_aspm_l11(pcie);
+               disable_aspm_l12(pcie);
+       }
+
+       val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
+       val &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
+       dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
+
+       /* Configure N_FTS & FTS */
+       val = dw_pcie_readl_dbi(pci, PORT_LOGIC_ACK_F_ASPM_CTRL);
+       val &= ~(N_FTS_MASK << N_FTS_SHIFT);
+       val |= N_FTS_VAL << N_FTS_SHIFT;
+       dw_pcie_writel_dbi(pci, PORT_LOGIC_ACK_F_ASPM_CTRL, val);
+
+       val = dw_pcie_readl_dbi(pci, PORT_LOGIC_GEN2_CTRL);
+       val &= ~FTS_MASK;
+       val |= FTS_VAL;
+       dw_pcie_writel_dbi(pci, PORT_LOGIC_GEN2_CTRL, val);
+
+       /* Configure Max Speed from DT */
+       if (pcie->max_speed && pcie->max_speed != -EINVAL) {
+               val = dw_pcie_readl_dbi(pci, pcie->pcie_cap_base +
+                                       PCI_EXP_LNKCAP);
+               val &= ~PCI_EXP_LNKCAP_SLS;
+               val |= pcie->max_speed;
+               dw_pcie_writel_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKCAP,
+                                  val);
+       }
+
+       pcie->pcie_cap_base = dw_pcie_find_capability(&pcie->pci,
+                                                     PCI_CAP_ID_EXP);
+       clk_set_rate(pcie->core_clk, GEN4_CORE_CLK_FREQ);
+
+       val = (ep->msi_mem_phys & MSIX_ADDR_MATCH_LOW_OFF_MASK);
+       val |= MSIX_ADDR_MATCH_LOW_OFF_EN;
+       dw_pcie_writel_dbi(pci, MSIX_ADDR_MATCH_LOW_OFF, val);
+       val = (lower_32_bits(ep->msi_mem_phys) & MSIX_ADDR_MATCH_HIGH_OFF_MASK);
+       dw_pcie_writel_dbi(pci, MSIX_ADDR_MATCH_HIGH_OFF, val);
+
+       ret = dw_pcie_ep_init_complete(ep);
+       if (ret) {
+               dev_err(dev, "Failed to complete initialization: %d\n", ret);
+               goto fail_init_complete;
+       }
+
+       dw_pcie_ep_init_notify(ep);
+
+       /* Enable LTSSM */
+       val = appl_readl(pcie, APPL_CTRL);
+       val |= APPL_CTRL_LTSSM_EN;
+       appl_writel(pcie, val, APPL_CTRL);
+
+       pcie->ep_state = EP_STATE_ENABLED;
+       dev_dbg(dev, "Initialization of endpoint is completed\n");
+
+       return;
+
+fail_init_complete:
+       reset_control_assert(pcie->core_rst);
+       tegra_pcie_disable_phy(pcie);
+fail_phy:
+       reset_control_assert(pcie->core_apb_rst);
+fail_core_apb_rst:
+       clk_disable_unprepare(pcie->core_clk);
+fail_core_clk_enable:
+       tegra_pcie_bpmp_set_pll_state(pcie, false);
+fail_pll_init:
+       pm_runtime_put_sync(dev);
+}
+
+static irqreturn_t tegra_pcie_ep_pex_rst_irq(int irq, void *arg)
+{
+       struct tegra_pcie_dw *pcie = arg;
+
+       if (gpiod_get_value(pcie->pex_rst_gpiod))
+               pex_ep_event_pex_rst_assert(pcie);
+       else
+               pex_ep_event_pex_rst_deassert(pcie);
+
+       return IRQ_HANDLED;
+}
+
+static int tegra_pcie_ep_raise_legacy_irq(struct tegra_pcie_dw *pcie, u16 irq)
+{
+       /* Tegra194 supports only INTA */
+       if (irq > 1)
+               return -EINVAL;
+
+       appl_writel(pcie, 1, APPL_LEGACY_INTX);
+       usleep_range(1000, 2000);
+       appl_writel(pcie, 0, APPL_LEGACY_INTX);
+       return 0;
+}
+
+static int tegra_pcie_ep_raise_msi_irq(struct tegra_pcie_dw *pcie, u16 irq)
+{
+       if (unlikely(irq > 31))
+               return -EINVAL;
+
+       appl_writel(pcie, (1 << irq), APPL_MSI_CTRL_1);
+
+       return 0;
+}
+
+static int tegra_pcie_ep_raise_msix_irq(struct tegra_pcie_dw *pcie, u16 irq)
+{
+       struct dw_pcie_ep *ep = &pcie->pci.ep;
+
+       writel(irq, ep->msi_mem);
+
+       return 0;
+}
+
+static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
+                                  enum pci_epc_irq_type type,
+                                  u16 interrupt_num)
+{
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+       struct tegra_pcie_dw *pcie = to_tegra_pcie(pci);
+
+       switch (type) {
+       case PCI_EPC_IRQ_LEGACY:
+               return tegra_pcie_ep_raise_legacy_irq(pcie, interrupt_num);
+
+       case PCI_EPC_IRQ_MSI:
+               return tegra_pcie_ep_raise_msi_irq(pcie, interrupt_num);
+
+       case PCI_EPC_IRQ_MSIX:
+               return tegra_pcie_ep_raise_msix_irq(pcie, interrupt_num);
+
+       default:
+               dev_err(pci->dev, "Unknown IRQ type\n");
+               return -EPERM;
+       }
+
+       return 0;
+}
+
+static const struct pci_epc_features tegra_pcie_epc_features = {
+       .linkup_notifier = true,
+       .core_init_notifier = true,
+       .msi_capable = false,
+       .msix_capable = false,
+       .reserved_bar = 1 << BAR_2 | 1 << BAR_3 | 1 << BAR_4 | 1 << BAR_5,
+       .bar_fixed_64bit = 1 << BAR_0,
+       .bar_fixed_size[0] = SZ_1M,
+};
+
+static const struct pci_epc_features*
+tegra_pcie_ep_get_features(struct dw_pcie_ep *ep)
+{
+       return &tegra_pcie_epc_features;
+}
+
+static struct dw_pcie_ep_ops pcie_ep_ops = {
+       .raise_irq = tegra_pcie_ep_raise_irq,
+       .get_features = tegra_pcie_ep_get_features,
+};
+
+static int tegra_pcie_config_ep(struct tegra_pcie_dw *pcie,
+                               struct platform_device *pdev)
+{
+       struct dw_pcie *pci = &pcie->pci;
+       struct device *dev = pcie->dev;
+       struct dw_pcie_ep *ep;
+       struct resource *res;
+       char *name;
+       int ret;
+
+       ep = &pci->ep;
+       ep->ops = &pcie_ep_ops;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
+       if (!res)
+               return -EINVAL;
+
+       ep->phys_base = res->start;
+       ep->addr_size = resource_size(res);
+       ep->page_size = SZ_64K;
+
+       ret = gpiod_set_debounce(pcie->pex_rst_gpiod, PERST_DEBOUNCE_TIME);
+       if (ret < 0) {
+               dev_err(dev, "Failed to set PERST GPIO debounce time: %d\n",
+                       ret);
+               return ret;
+       }
+
+       ret = gpiod_to_irq(pcie->pex_rst_gpiod);
+       if (ret < 0) {
+               dev_err(dev, "Failed to get IRQ for PERST GPIO: %d\n", ret);
+               return ret;
+       }
+       pcie->pex_rst_irq = (unsigned int)ret;
+
+       name = devm_kasprintf(dev, GFP_KERNEL, "tegra_pcie_%u_pex_rst_irq",
+                             pcie->cid);
+       if (!name) {
+               dev_err(dev, "Failed to create PERST IRQ string\n");
+               return -ENOMEM;
+       }
+
+       irq_set_status_flags(pcie->pex_rst_irq, IRQ_NOAUTOEN);
+
+       pcie->ep_state = EP_STATE_DISABLED;
+
+       ret = devm_request_threaded_irq(dev, pcie->pex_rst_irq, NULL,
+                                       tegra_pcie_ep_pex_rst_irq,
+                                       IRQF_TRIGGER_RISING |
+                                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                       name, (void *)pcie);
+       if (ret < 0) {
+               dev_err(dev, "Failed to request IRQ for PERST: %d\n", ret);
+               return ret;
+       }
+
+       name = devm_kasprintf(dev, GFP_KERNEL, "tegra_pcie_%u_ep_work",
+                             pcie->cid);
+       if (!name) {
+               dev_err(dev, "Failed to create PCIe EP work thread string\n");
+               return -ENOMEM;
+       }
+
+       pm_runtime_enable(dev);
+
+       ret = dw_pcie_ep_init(ep);
+       if (ret) {
+               dev_err(dev, "Failed to initialize DWC Endpoint subsystem: %d\n",
+                       ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 static int tegra_pcie_dw_probe(struct platform_device *pdev)
 {
+       const struct tegra_pcie_dw_of_data *data;
        struct device *dev = &pdev->dev;
        struct resource *atu_dma_res;
        struct tegra_pcie_dw *pcie;
@@ -1440,6 +2044,8 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev)
        int ret;
        u32 i;
 
+       data = of_device_get_match_data(dev);
+
        pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
@@ -1449,6 +2055,7 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev)
        pci->ops = &tegra_dw_pcie_ops;
        pp = &pci->pp;
        pcie->dev = &pdev->dev;
+       pcie->mode = (enum dw_pcie_device_mode)data->mode;
 
        ret = tegra_pcie_dw_parse_dt(pcie);
        if (ret < 0) {
@@ -1462,6 +2069,9 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev)
                return ret;
        }
 
+       if (pcie->pex_refclk_sel_gpiod)
+               gpiod_set_value(pcie->pex_refclk_sel_gpiod, 1);
+
        pcie->pex_ctl_supply = devm_regulator_get(dev, "vddio-pex-ctl");
        if (IS_ERR(pcie->pex_ctl_supply)) {
                ret = PTR_ERR(pcie->pex_ctl_supply);
@@ -1557,24 +2167,49 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       ret = devm_request_irq(dev, pp->irq, tegra_pcie_irq_handler,
-                              IRQF_SHARED, "tegra-pcie-intr", pcie);
-       if (ret) {
-               dev_err(dev, "Failed to request IRQ %d: %d\n", pp->irq, ret);
-               return ret;
-       }
-
        pcie->bpmp = tegra_bpmp_get(dev);
        if (IS_ERR(pcie->bpmp))
                return PTR_ERR(pcie->bpmp);
 
        platform_set_drvdata(pdev, pcie);
 
-       ret = tegra_pcie_config_rp(pcie);
-       if (ret && ret != -ENOMEDIUM)
-               goto fail;
-       else
-               return 0;
+       switch (pcie->mode) {
+       case DW_PCIE_RC_TYPE:
+               ret = devm_request_irq(dev, pp->irq, tegra_pcie_rp_irq_handler,
+                                      IRQF_SHARED, "tegra-pcie-intr", pcie);
+               if (ret) {
+                       dev_err(dev, "Failed to request IRQ %d: %d\n", pp->irq,
+                               ret);
+                       goto fail;
+               }
+
+               ret = tegra_pcie_config_rp(pcie);
+               if (ret && ret != -ENOMEDIUM)
+                       goto fail;
+               else
+                       return 0;
+               break;
+
+       case DW_PCIE_EP_TYPE:
+               ret = devm_request_threaded_irq(dev, pp->irq,
+                                               tegra_pcie_ep_hard_irq,
+                                               tegra_pcie_ep_irq_thread,
+                                               IRQF_SHARED | IRQF_ONESHOT,
+                                               "tegra-pcie-ep-intr", pcie);
+               if (ret) {
+                       dev_err(dev, "Failed to request IRQ %d: %d\n", pp->irq,
+                               ret);
+                       goto fail;
+               }
+
+               ret = tegra_pcie_config_ep(pcie, pdev);
+               if (ret < 0)
+                       goto fail;
+               break;
+
+       default:
+               dev_err(dev, "Invalid PCIe device type %d\n", pcie->mode);
+       }
 
 fail:
        tegra_bpmp_put(pcie->bpmp);
@@ -1593,6 +2228,8 @@ static int tegra_pcie_dw_remove(struct platform_device *pdev)
        pm_runtime_put_sync(pcie->dev);
        pm_runtime_disable(pcie->dev);
        tegra_bpmp_put(pcie->bpmp);
+       if (pcie->pex_refclk_sel_gpiod)
+               gpiod_set_value(pcie->pex_refclk_sel_gpiod, 0);
 
        return 0;
 }
@@ -1697,9 +2334,22 @@ static void tegra_pcie_dw_shutdown(struct platform_device *pdev)
        __deinit_controller(pcie);
 }
 
+static const struct tegra_pcie_dw_of_data tegra_pcie_dw_rc_of_data = {
+       .mode = DW_PCIE_RC_TYPE,
+};
+
+static const struct tegra_pcie_dw_of_data tegra_pcie_dw_ep_of_data = {
+       .mode = DW_PCIE_EP_TYPE,
+};
+
 static const struct of_device_id tegra_pcie_dw_of_match[] = {
        {
                .compatible = "nvidia,tegra194-pcie",
+               .data = &tegra_pcie_dw_rc_of_data,
+       },
+       {
+               .compatible = "nvidia,tegra194-pcie-ep",
+               .data = &tegra_pcie_dw_ep_of_data,
        },
        {},
 };