diff --git a/drivers/pci/controller/private-soc/Makefile b/drivers/pci/controller/private-soc/Makefile index 5f8346b9..466e08f8 100644 --- a/drivers/pci/controller/private-soc/Makefile +++ b/drivers/pci/controller/private-soc/Makefile @@ -3,6 +3,8 @@ # Set config when build as OOT module. ifeq ($(CONFIG_TEGRA_OOT_MODULE),m) CONFIG_PCIE_TEGRA264 := m +CONFIG_PCIE_TEGRA264_EP := m endif obj-$(CONFIG_PCIE_TEGRA264) += pcie-tegra264.o +obj-$(CONFIG_PCIE_TEGRA264_EP) += pcie-tegra264-ep.o diff --git a/drivers/pci/controller/private-soc/pcie-tegra264-ep.c b/drivers/pci/controller/private-soc/pcie-tegra264-ep.c new file mode 100644 index 00000000..57981dbd --- /dev/null +++ b/drivers/pci/controller/private-soc/pcie-tegra264-ep.c @@ -0,0 +1,834 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PCIe EP controller driver for Tegra264 SoC + * + * Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. + * + * Author: Manikanta Maddireddy + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 5 msec PERST# gpio debounce */ +#define PERST_DEBOUNCE_TIME 5000 + +/* Poll for every 10 msec */ +#define PCIE_CBB_IDLE_DELAY 10000 +/* 1 sec timeout */ +#define PCIE_CBB_IDLE_TIMEOUT 1000000 + +/* Poll for every 10 msec */ +#define PCIE_LTSSM_DELAY 10000 +/* 1 sec timeout */ +#define PCIE_LTSSM_TIMEOUT 1000000 + +/* PCIe EP support 8 outbound ATU windows */ +#define NUM_OB_WINDOWS 8 + +#define EP_STATE_DISABLED 0 +#define EP_STATE_ENABLED 1 + +#define XAL_RC_PCIX_INTR_STATUS 0x200 +#define XAL_RC_PCIX_INTR_STATUS_EP_RESET BIT(8) + +#define XAL_RC_PCIX_INTR_EN 0x204 +#define XAL_RC_PCIX_INTR_EN_EP_L2 BIT(9) +#define XAL_RC_PCIX_INTR_EN_EP_RESET BIT(8) + +#define XAL_RC_DEBUG_HB_REG_2 0x514 +#define XAL_RC_DEBUG_HB_REG_2_HB_HIER_IDLE BIT(29) + +#define XTL_EP_PRI_BAR_CONFIG 0x214 +#define XTL_EP_PRI_BAR_CONFIG_BAR2_EN BIT(2) +#define XTL_EP_PRI_BAR_CONFIG_BAR1_EN BIT(11) + +#define XTL_EP_PRI_CLASSCODE_OVERRIDE 0x218 +#define XTL_EP_PRI_CLASSCODE_OVERRIDE_VAL GENMASK(7, 0) + +#define XTL_EP_PRI_RESIZE_BAR1 0x278 + +#define XTL_EP_PRI_FUNC_INTR_STATUS 0x53c +#define XTL_EP_PRI_FUNC_INTR_STATUS_L2_ENTRY BIT(1) + +#define XTL_EP_PRI_SW_MSG_DW0 0xa1c +#define XTL_EP_PRI_SW_MSG_DW0_PME_TO_ACK 0x15 + +#define XTL_EP_PRI_SW_MSG_DW1 0xa20 +#define XTL_EP_PRI_SW_MSG_DW1_PME_TO_ACK 0x1b + +#define XTL_EP_PRI_SW_MSG_CTRL 0xa18 +#define XTL_EP_PRI_SW_MSG_CTRL_TRIGGER BIT(0) + +#define XTL_EP_PRI_PWR_MGMT_CTRL 0xb20 +#define XTL_EP_PRI_PWR_MGMT_CTRL_DIS_PME_TO_ACK BIT(0) + +#define XTL_EP_PRI_DEVID 0xc80 +#define XTL_EP_PRI_DEVID_OVERIDE GENMASK(6, 0) +#define XTL_EP_PRI_DEVID_SW_UPDATE_PROD_ID BIT(7) + +#define XTL_EP_CFG_INT 0xe80 +#define XTL_EP_CFG_INT_INTX_SET BIT(0) +#define XTL_EP_CFG_INT_MSI_SET BIT(1) +#define XTL_EP_CFG_INT_MSI_VEC_ID_SHIFT 2 + +#define XAL_EP_DM_I_ATU_REDIR_BAR1_ADDR_LSB 0x700 +#define XAL_EP_DM_I_ATU_REDIR_BAR1_ADDR_MSB 0x704 +#define XAL_EP_DM_I_ATU_REDIR_BAR2_ADDR_LSB 0x708 +#define XAL_EP_DM_I_ATU_REDIR_BAR2_ADDR_MSB 0x70c + +#define XAL_EP_DM_I_ATU_REDIR_CTRL 0x800 +#define XAL_EP_DM_I_ATU_REDIR_CTRL_BAR1_EN BIT(0) +#define XAL_EP_DM_I_ATU_REDIR_CTRL_BAR2_EN BIT(4) + +#define NV_XAL_EP_DM_E_ATU_LOCAL_BASE_HI(idx) (0xb40 + (0x20 * (idx))) +#define NV_XAL_EP_DM_E_ATU_LOCAL_BASE_LO(idx) (0xb44 + (0x20 * (idx))) +#define NV_XAL_EP_DM_E_ATU_LOCAL_LIMIT_HI(idx) (0xb48 + (0x20 * (idx))) +#define NV_XAL_EP_DM_E_ATU_LOCAL_LIMIT_LO(idx) (0xb4c + (0x20 * (idx))) +#define NV_XAL_EP_DM_E_ATU_REMOTE_TARGET_HI(idx) (0xb50 + (0x20 * (idx))) +#define NV_XAL_EP_DM_E_ATU_REMOTE_TARGET_LO(idx) (0xb54 + (0x20 * (idx))) + +#define XPL_PL_LTSSM_STATE 0x1700 +#define XPL_PL_LTSSM_STATE_FULL GENMASK(7, 0) +#define XPL_PL_LTSSM_STATE_DETECT_RESET 0 + +#define NV_EP_PCFG_MSI_64_HEADER 0x48 + +struct tegra264_pcie_ep { + struct device *dev; + + void __iomem *xal_base; + void __iomem *xpl_base; + void __iomem *xal_ep_dm_base; + void __iomem *xtl_ep_pri_base; + void __iomem *xtl_ep_cfg_base; + + struct gpio_desc *pex_prsnt_gpiod; + struct gpio_desc *pex_rst_gpiod; + unsigned int pex_rst_irq; + + phys_addr_t phys_base; + size_t phys_size; + size_t page_size; + dma_addr_t bar1_phys; + dma_addr_t bar2_phys; + + struct pci_epc *epc; + struct tegra_bpmp *bpmp; + + phys_addr_t *ob_addr; + unsigned long *ob_window_map; + + u32 ctl_id; + u32 ep_state; + u8 progif_code; + u8 subclass_code; + u8 baseclass_code; + u8 deviceid; + u8 bar1_size; +}; + +static int tegra264_pcie_bpmp_set_ep_state(struct tegra264_pcie_ep *pcie, bool enable) +{ + struct tegra_bpmp_message msg; + struct mrq_pcie_request req; + int err; + + memset(&req, 0, sizeof(req)); + + if (enable) { + req.cmd = CMD_PCIE_EP_CONTROLLER_INIT; + req.ep_ctrlr_init.ep_controller = pcie->ctl_id; + req.ep_ctrlr_init.progif_code = pcie->progif_code; + req.ep_ctrlr_init.subclass_code = pcie->subclass_code; + req.ep_ctrlr_init.baseclass_code = pcie->baseclass_code; + req.ep_ctrlr_init.deviceid = pcie->deviceid; + req.ep_ctrlr_init.bar1_size = pcie->bar1_size; + } else { + req.cmd = CMD_PCIE_EP_CONTROLLER_OFF; + req.ep_ctrlr_off.ep_controller = pcie->ctl_id; + } + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_PCIE; + msg.tx.data = &req; + msg.tx.size = sizeof(req); + + err = tegra_bpmp_transfer(pcie->bpmp, &msg); + if (err) + return err; + if (msg.rx.ret) + return -EINVAL; + + return 0; +} + +static void tegra264_pcie_ep_rst_assert(struct tegra264_pcie_ep *pcie) +{ + u32 val; + int ret; + + if (pcie->ep_state == EP_STATE_DISABLED) + return; + + /* Endpoint is going away, assert PRSNT# to mask EP from RP until it is ready link up */ + if (pcie->pex_prsnt_gpiod) + gpiod_set_value_cansleep(pcie->pex_prsnt_gpiod, 0); + + pci_epc_deinit_notify(pcie->epc); + + ret = readl_poll_timeout(pcie->xal_base + XAL_RC_DEBUG_HB_REG_2, val, + val & XAL_RC_DEBUG_HB_REG_2_HB_HIER_IDLE, + PCIE_CBB_IDLE_DELAY, PCIE_CBB_IDLE_TIMEOUT); + if (ret) + dev_err(pcie->dev, "PCIe CBB idle timedout: %d\n", ret); + + /* When stopping the Endpoint, clear outbound mappings. */ + bitmap_zero(pcie->ob_window_map, NUM_OB_WINDOWS); + + ret = tegra264_pcie_bpmp_set_ep_state(pcie, false); + if (ret) + dev_err(pcie->dev, "Failed to turn off PCIe: %d\n", ret); + + pcie->ep_state = EP_STATE_DISABLED; + + dev_dbg(pcie->dev, "Uninitialization of endpoint is completed\n"); +} + +static void tegra264_pcie_ep_config_bar(struct tegra264_pcie_ep *pcie) +{ + u32 val; + + writel(upper_32_bits(pcie->bar1_phys), + pcie->xal_ep_dm_base + XAL_EP_DM_I_ATU_REDIR_BAR1_ADDR_MSB); + writel(lower_32_bits(pcie->bar1_phys), + pcie->xal_ep_dm_base + XAL_EP_DM_I_ATU_REDIR_BAR1_ADDR_LSB); + + writel(upper_32_bits(pcie->bar2_phys), + pcie->xal_ep_dm_base + XAL_EP_DM_I_ATU_REDIR_BAR2_ADDR_MSB); + writel(lower_32_bits(pcie->bar2_phys), + pcie->xal_ep_dm_base + XAL_EP_DM_I_ATU_REDIR_BAR2_ADDR_LSB); + + val = readl(pcie->xal_ep_dm_base + XAL_EP_DM_I_ATU_REDIR_CTRL); + val |= (XAL_EP_DM_I_ATU_REDIR_CTRL_BAR1_EN | XAL_EP_DM_I_ATU_REDIR_CTRL_BAR2_EN); + writel(val, pcie->xal_ep_dm_base + XAL_EP_DM_I_ATU_REDIR_CTRL); +} + +static void tegra264_pcie_ep_rst_deassert(struct tegra264_pcie_ep *pcie) +{ + struct device *dev = pcie->dev; + int ret; + u32 val; + + if (pcie->ep_state == EP_STATE_ENABLED) + return; + + ret = tegra264_pcie_bpmp_set_ep_state(pcie, true); + if (ret) { + dev_err(dev, "Failed to initialize PCIe endpoint: %d\n", ret); + return; + } + + tegra264_pcie_ep_config_bar(pcie); + + val = readl(pcie->xal_base + XAL_RC_PCIX_INTR_EN); + val |= XAL_RC_PCIX_INTR_EN_EP_L2 | XAL_RC_PCIX_INTR_EN_EP_RESET; + writel(val, pcie->xal_base + XAL_RC_PCIX_INTR_EN); + + writel(XTL_EP_PRI_PWR_MGMT_CTRL_DIS_PME_TO_ACK, + pcie->xtl_ep_pri_base + XTL_EP_PRI_PWR_MGMT_CTRL); + + pcie->ep_state = EP_STATE_ENABLED; + + dev_dbg(dev, "Initialization of endpoint is completed\n"); +} + +static irqreturn_t tegra264_pcie_ep_rst_irq(int irq, void *arg) +{ + struct tegra264_pcie_ep *pcie = arg; + + if (gpiod_get_value(pcie->pex_rst_gpiod)) + tegra264_pcie_ep_rst_assert(pcie); + else + tegra264_pcie_ep_rst_deassert(pcie); + + return IRQ_HANDLED; +} + +static int tegra264_pcie_ep_write_header(struct pci_epc *epc, u8 fn, u8 vfn, + struct pci_epf_header *hdr) +{ + struct tegra264_pcie_ep *pcie = epc_get_drvdata(epc); + + pcie->deviceid = (u8)(hdr->deviceid & 0x7F); + pcie->baseclass_code = hdr->baseclass_code; + pcie->subclass_code = hdr->subclass_code; + pcie->progif_code = hdr->progif_code; + + return 0; +} + +static int tegra264_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, u8 vfn, struct pci_epf_bar *epf_bar) +{ + struct tegra264_pcie_ep *pcie = epc_get_drvdata(epc); + enum pci_barno bar = epf_bar->barno; + size_t size = epf_bar->size; + u32 sz_bitmap; + + if ((epf_bar->flags & 0xf) != + (PCI_BASE_ADDRESS_MEM_TYPE_64 | PCI_BASE_ADDRESS_MEM_PREFETCH)) { + dev_err(pcie->dev, "%s: Only 64-bit prefectable BAR is supported, flags: 0x%x\n", + __func__, epf_bar->flags); + return -EINVAL; + } + + if (bar == BAR_1) { + /* Fail set BAR if size is not power of 2 or out of [64MB,64GB] range */ + if (!((size >= SZ_64M && size <= (SZ_32G * 2)) && !(size & (size - 1)))) { + dev_err(pcie->dev, "%s: Invalid BAR1 size requested: %ld\n", + __func__, size); + return -EINVAL; + } + pcie->bar1_phys = epf_bar->phys_addr; + /* Convert from bytes to MB in bitmap encoding */ + sz_bitmap = ilog2(size) - 20; + pcie->bar1_size = (u8)(sz_bitmap & 0xFF); + } else if (bar == BAR_2) { + if (size != SZ_32M) { + dev_err(pcie->dev, "%s: Only 32MB BAR2 is supported, requested size: %ld\n", + __func__, size); + return -EINVAL; + } + pcie->bar2_phys = epf_bar->phys_addr; + } else { + dev_err(pcie->dev, "%s: BAR-%d is not configurable\n", __func__, bar); + return -EINVAL; + } + + return 0; +} + +static void tegra264_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, u8 vfn, + struct pci_epf_bar *epf_bar) +{ + struct tegra264_pcie_ep *pcie = epc_get_drvdata(epc); + enum pci_barno bar = epf_bar->barno; + u32 val; + + if (bar == BAR_1) { + writel(0x0, pcie->xtl_ep_pri_base + XTL_EP_PRI_RESIZE_BAR1); + + writel(0x0, pcie->xal_ep_dm_base + XAL_EP_DM_I_ATU_REDIR_BAR1_ADDR_MSB); + writel(0x0, pcie->xal_ep_dm_base + XAL_EP_DM_I_ATU_REDIR_BAR1_ADDR_LSB); + + val = readl(pcie->xal_ep_dm_base + XAL_EP_DM_I_ATU_REDIR_CTRL); + val &= ~XAL_EP_DM_I_ATU_REDIR_CTRL_BAR1_EN; + writel(val, pcie->xal_ep_dm_base + XAL_EP_DM_I_ATU_REDIR_CTRL); + } else if (bar == BAR_2) { + val = readl(pcie->xtl_ep_pri_base + XTL_EP_PRI_BAR_CONFIG); + writel(val, pcie->xtl_ep_pri_base + XTL_EP_PRI_BAR_CONFIG); + + writel(0x0, pcie->xal_ep_dm_base + XAL_EP_DM_I_ATU_REDIR_BAR2_ADDR_MSB); + writel(0x0, pcie->xal_ep_dm_base + XAL_EP_DM_I_ATU_REDIR_BAR2_ADDR_LSB); + + val = readl(pcie->xal_ep_dm_base + XAL_EP_DM_I_ATU_REDIR_CTRL); + val &= ~XAL_EP_DM_I_ATU_REDIR_CTRL_BAR2_EN; + writel(val, pcie->xal_ep_dm_base + XAL_EP_DM_I_ATU_REDIR_CTRL); + } + + val = readl(pcie->xtl_ep_pri_base + XTL_EP_PRI_BAR_CONFIG); + val &= ~(XTL_EP_PRI_BAR_CONFIG_BAR1_EN | XTL_EP_PRI_BAR_CONFIG_BAR2_EN); + writel(val, pcie->xtl_ep_pri_base + XTL_EP_PRI_BAR_CONFIG); +} + +static int tegra264_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + phys_addr_t addr, u64 pci_addr, size_t size) +{ + struct tegra264_pcie_ep *pcie = epc_get_drvdata(epc); + phys_addr_t limit = addr + size - 1; + u32 free_win; + + free_win = find_first_zero_bit(pcie->ob_window_map, NUM_OB_WINDOWS); + if (free_win >= NUM_OB_WINDOWS) { + dev_err(pcie->dev, "No free outbound window\n"); + return -EINVAL; + } + + writel(upper_32_bits(addr), + pcie->xal_ep_dm_base + NV_XAL_EP_DM_E_ATU_LOCAL_BASE_HI(free_win)); + writel(lower_32_bits(addr), + pcie->xal_ep_dm_base + NV_XAL_EP_DM_E_ATU_LOCAL_BASE_LO(free_win)); + writel(upper_32_bits(limit), + pcie->xal_ep_dm_base + NV_XAL_EP_DM_E_ATU_LOCAL_LIMIT_HI(free_win)); + writel(lower_32_bits(limit), + pcie->xal_ep_dm_base + NV_XAL_EP_DM_E_ATU_LOCAL_LIMIT_LO(free_win)); + writel(upper_32_bits(pci_addr), + pcie->xal_ep_dm_base + NV_XAL_EP_DM_E_ATU_REMOTE_TARGET_HI(free_win)); + writel(lower_32_bits(pci_addr), + pcie->xal_ep_dm_base + NV_XAL_EP_DM_E_ATU_REMOTE_TARGET_LO(free_win)); + + set_bit(free_win, pcie->ob_window_map); + pcie->ob_addr[free_win] = addr; + + return 0; +} + +static void tegra264_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + phys_addr_t addr) +{ + struct tegra264_pcie_ep *pcie = epc_get_drvdata(epc); + u32 ob_idx; + + for (ob_idx = 0; ob_idx < NUM_OB_WINDOWS; ob_idx++) { + if (pcie->ob_addr[ob_idx] != addr) + continue; + break; + } + + if (ob_idx == NUM_OB_WINDOWS) + return; + + writel(0x0, pcie->xal_ep_dm_base + NV_XAL_EP_DM_E_ATU_LOCAL_BASE_HI(ob_idx)); + writel(0x0, pcie->xal_ep_dm_base + NV_XAL_EP_DM_E_ATU_LOCAL_BASE_LO(ob_idx)); + writel(0x0, pcie->xal_ep_dm_base + NV_XAL_EP_DM_E_ATU_LOCAL_LIMIT_HI(ob_idx)); + writel(0x0, pcie->xal_ep_dm_base + NV_XAL_EP_DM_E_ATU_LOCAL_LIMIT_LO(ob_idx)); + writel(0x0, pcie->xal_ep_dm_base + NV_XAL_EP_DM_E_ATU_REMOTE_TARGET_HI(ob_idx)); + writel(0x0, pcie->xal_ep_dm_base + NV_XAL_EP_DM_E_ATU_REMOTE_TARGET_LO(ob_idx)); + + clear_bit(ob_idx, pcie->ob_window_map); +} + +static int tegra264_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, u8 vfn, u8 mmc) +{ + /* + * Tegra264 PCIe EP HW supports 16 MSIs only, return success if multi msg cap encoded + * value is <= 4 without programming MSI capability register since we can enable + * desired number of MSIs even if more MSIs are supported. This will avoid unnecessary + * failure of pci_epc_set_msi() in EPF driver. If more than 16 MSIs are requested then + * return error. + */ + if (mmc <= 4) + return 0; + else + return -EINVAL; +} + +static int tegra264_pcie_ep_get_msi(struct pci_epc *epc, u8 fn, u8 vfn) +{ + struct tegra264_pcie_ep *pcie = epc_get_drvdata(epc); + u32 val; + + val = readw(pcie->xtl_ep_cfg_base + NV_EP_PCFG_MSI_64_HEADER + PCI_MSI_FLAGS); + if (!(val & PCI_MSI_FLAGS_ENABLE)) + return -EINVAL; + + val = (val & PCI_MSI_FLAGS_QSIZE) >> 4; + + return val; +} + +static int tegra264_pcie_ep_send_legacy_irq(struct tegra264_pcie_ep *pcie, u8 fn) +{ + u32 val; + + val = XTL_EP_CFG_INT_INTX_SET; + writel(val, pcie->xtl_ep_pri_base + XTL_EP_CFG_INT); + mdelay(1); + val &= ~XTL_EP_CFG_INT_INTX_SET; + writel(val, pcie->xtl_ep_pri_base + XTL_EP_CFG_INT); + + return 0; +} + +static int tegra264_pcie_ep_send_msi_irq(struct tegra264_pcie_ep *pcie, u8 fn, u8 irq_num) +{ + u32 val; + + val = XTL_EP_CFG_INT_MSI_SET | (irq_num << XTL_EP_CFG_INT_MSI_VEC_ID_SHIFT); + writel(val, pcie->xtl_ep_pri_base + XTL_EP_CFG_INT); + + return 0; +} + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)) +static int tegra264_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, u8 vfn, + enum pci_epc_irq_type type, u16 irq_num) +#else +static int tegra264_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, u8 vfn, + unsigned int type, u16 irq_num) +#endif +{ + struct tegra264_pcie_ep *pcie = epc_get_drvdata(epc); + + /* + * Legacy INTX and MSI are generated by writing to same register. But We don't need to + * synchronize these functions because we don't expect both interrupts enabled at the + * same time. + */ + switch (type) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)) + case PCI_EPC_IRQ_LEGACY: +#else + case PCI_IRQ_INTX: +#endif + /* Only INTA is supported. */ + return tegra264_pcie_ep_send_legacy_irq(pcie, fn); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)) + case PCI_EPC_IRQ_MSI: +#else + case PCI_IRQ_MSI: +#endif + return tegra264_pcie_ep_send_msi_irq(pcie, fn, irq_num); + default: + return -EINVAL; + } +} + +static int tegra264_pcie_ep_start(struct pci_epc *epc) +{ + struct tegra264_pcie_ep *pcie = epc_get_drvdata(epc); + + if (pcie->pex_prsnt_gpiod) + gpiod_set_value_cansleep(pcie->pex_prsnt_gpiod, 1); + + return 0; +} + +static void tegra264_pcie_ep_stop(struct pci_epc *epc) +{ + struct tegra264_pcie_ep *pcie = epc_get_drvdata(epc); + + if (pcie->pex_prsnt_gpiod) + gpiod_set_value_cansleep(pcie->pex_prsnt_gpiod, 0); +} + +static const struct pci_epc_features tegra264_pcie_epc_features = { + .linkup_notifier = true, + .core_init_notifier = false, + .msi_capable = true, + .msix_capable = false, + .reserved_bar = 1 << BAR_0 | 1 << BAR_3 | 1 << BAR_4 | 1 << BAR_5, + .bar_fixed_size[2] = SZ_32M, +}; + +static const struct pci_epc_features *tegra264_pcie_ep_get_features(struct pci_epc *epc, + u8 fn, u8 vfn) +{ + return &tegra264_pcie_epc_features; +} + +static const struct pci_epc_ops tegra264_pcie_epc_ops = { + .write_header = tegra264_pcie_ep_write_header, + .set_bar = tegra264_pcie_ep_set_bar, + .clear_bar = tegra264_pcie_ep_clear_bar, + .map_addr = tegra264_pcie_ep_map_addr, + .unmap_addr = tegra264_pcie_ep_unmap_addr, + .set_msi = tegra264_pcie_ep_set_msi, + .get_msi = tegra264_pcie_ep_get_msi, + .raise_irq = tegra264_pcie_ep_raise_irq, + .start = tegra264_pcie_ep_start, + .stop = tegra264_pcie_ep_stop, + .get_features = tegra264_pcie_ep_get_features, +}; + +static irqreturn_t tegra264_pcie_ep_irq_thread(int irq, void *arg) +{ + struct tegra264_pcie_ep *pcie = arg; + u32 val; + int ret; + + ret = readl_poll_timeout(pcie->xpl_base + XPL_PL_LTSSM_STATE, val, + (val & XPL_PL_LTSSM_STATE_FULL) == XPL_PL_LTSSM_STATE_DETECT_RESET, + PCIE_LTSSM_DELAY, PCIE_LTSSM_TIMEOUT); + if (ret) + dev_err(pcie->dev, "PCIe LTSSM state not in detect reset, ltssm: 0x%x\n", val); + + tegra264_pcie_ep_rst_assert(pcie); + tegra264_pcie_ep_rst_deassert(pcie); + + return IRQ_HANDLED; +} + +static irqreturn_t tegra264_pcie_ep_irq_handler(int irq, void *arg) +{ + struct tegra264_pcie_ep *pcie = arg; + u32 val; + + val = readl(pcie->xtl_ep_pri_base + XTL_EP_PRI_FUNC_INTR_STATUS); + writel(val, pcie->xtl_ep_pri_base + XTL_EP_PRI_FUNC_INTR_STATUS); + + /* Send PME Turnoff Ack */ + if (val & XTL_EP_PRI_FUNC_INTR_STATUS_L2_ENTRY) { + writel(XTL_EP_PRI_SW_MSG_DW0_PME_TO_ACK, + pcie->xtl_ep_pri_base + XTL_EP_PRI_SW_MSG_DW0); + writel(XTL_EP_PRI_SW_MSG_DW1_PME_TO_ACK, + pcie->xtl_ep_pri_base + XTL_EP_PRI_SW_MSG_DW1); + writel(XTL_EP_PRI_SW_MSG_CTRL_TRIGGER, + pcie->xtl_ep_pri_base + XTL_EP_PRI_SW_MSG_CTRL); + } + + val = readl(pcie->xal_base + XAL_RC_PCIX_INTR_STATUS); + writel(val, pcie->xal_base + XAL_RC_PCIX_INTR_STATUS); + + /* Wake irq thread to handle EP_RESET(SBR) */ + if (val & XAL_RC_PCIX_INTR_STATUS_EP_RESET) + return IRQ_WAKE_THREAD; + + return IRQ_HANDLED; +} + +static int tegra264_pcie_ep_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra264_pcie_ep *pcie; + struct resource *res; + char *name, *level; + int ret = 0, irq; + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + platform_set_drvdata(pdev, pcie); + pcie->dev = dev; + + pcie->ob_window_map = devm_bitmap_zalloc(dev, NUM_OB_WINDOWS, GFP_KERNEL); + if (!pcie->ob_window_map) + return -ENOMEM; + + ret = of_property_read_u32_index(dev->of_node, "nvidia,bpmp", 1, &pcie->ctl_id); + if (ret) { + dev_err(dev, "Failed to read Controller-ID: %d\n", ret); + return ret; + } + + ret = pinctrl_pm_select_default_state(dev); + if (ret < 0) { + dev_err(dev, "Failed to configure sideband pins: %d\n", ret); + return ret; + } + + pcie->pex_prsnt_gpiod = devm_gpiod_get_optional(dev, "nvidia,pex-prsnt", GPIOD_OUT_LOW); + if (IS_ERR(pcie->pex_prsnt_gpiod)) { + ret = PTR_ERR(pcie->pex_prsnt_gpiod); + if (ret == -EPROBE_DEFER) + return ret; + + dev_dbg(dev, "Failed to get PCIe PRSNT GPIO: %d\n", ret); + } + + pcie->pex_rst_gpiod = devm_gpiod_get(dev, "reset", GPIOD_IN); + if (IS_ERR(pcie->pex_rst_gpiod)) { + ret = PTR_ERR(pcie->pex_rst_gpiod); + if (ret == -EPROBE_DEFER) + level = KERN_DEBUG; + else + level = KERN_ERR; + + dev_printk(level, dev, dev_fmt("Failed to get PERST GPIO: %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; + + 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; + } + + name = devm_kasprintf(dev, GFP_KERNEL, "tegra264_pcie_%u_rst_irq", pcie->ctl_id); + if (!name) { + dev_err(dev, "Failed to create PERST IRQ string\n"); + return -ENOMEM; + } + + ret = devm_request_threaded_irq(dev, pcie->pex_rst_irq, NULL, tegra264_pcie_ep_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; + } + + pcie->xal_base = devm_platform_ioremap_resource_byname(pdev, "xal"); + if (IS_ERR(pcie->xal_base)) { + ret = PTR_ERR(pcie->xal_base); + dev_err(dev, "failed to map xal memory: %d\n", ret); + return ret; + } + + pcie->xpl_base = devm_platform_ioremap_resource_byname(pdev, "xpl"); + if (IS_ERR(pcie->xpl_base)) { + ret = PTR_ERR(pcie->xpl_base); + dev_err(dev, "failed to map xpl memory: %d\n", ret); + return ret; + } + + pcie->xal_ep_dm_base = devm_platform_ioremap_resource_byname(pdev, "xal-ep-dm"); + if (IS_ERR(pcie->xal_ep_dm_base)) { + ret = PTR_ERR(pcie->xal_ep_dm_base); + dev_err(dev, "failed to map xal ep dm memory: %d\n", ret); + return ret; + } + + pcie->xtl_ep_pri_base = devm_platform_ioremap_resource_byname(pdev, "xtl-ep-pri"); + if (IS_ERR(pcie->xtl_ep_pri_base)) { + ret = PTR_ERR(pcie->xtl_ep_pri_base); + dev_err(dev, "failed to map xtl ep pri memory: %d\n", ret); + return ret; + } + + pcie->xtl_ep_cfg_base = devm_platform_ioremap_resource_byname(pdev, "xtl-ep-cfg"); + if (IS_ERR(pcie->xtl_ep_cfg_base)) { + ret = PTR_ERR(pcie->xtl_ep_cfg_base); + dev_err(dev, "failed to map xtl ep cfg memory: %d\n", ret); + return ret; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space"); + if (!res) { + dev_err(dev, "faled to get addr_space resource\n"); + return -EINVAL; + } + + pcie->phys_base = res->start; + pcie->phys_size = resource_size(res); + pcie->page_size = SZ_64K; + + pcie->epc = devm_pci_epc_create(dev, &tegra264_pcie_epc_ops); + if (IS_ERR(pcie->epc)) { + dev_err(dev, "failed to create epc device\n"); + return PTR_ERR(pcie->epc); + } + pcie->epc->max_functions = 1; + + ret = pci_epc_mem_init(pcie->epc, pcie->phys_base, pcie->phys_size, pcie->page_size); + if (ret < 0) { + dev_err(dev, "failed to initialize the memory space\n"); + return ret; + } + + pcie->bpmp = tegra_bpmp_get(dev); + if (IS_ERR(pcie->bpmp)) { + dev_err(dev, "tegra_bpmp_get fail: %ld\n", PTR_ERR(pcie->bpmp)); + ret = PTR_ERR(pcie->bpmp); + goto fail_get_bpmp; + } + + epc_set_drvdata(pcie->epc, pcie); + pcie->ep_state = EP_STATE_DISABLED; + + irq = platform_get_irq_byname(pdev, "intr"); + if (irq < 0) { + dev_err(dev, "failed to get intr: %d\n", irq); + ret = irq; + goto fail_get_irq; + } + + ret = devm_request_threaded_irq(dev, irq, tegra264_pcie_ep_irq_handler, + tegra264_pcie_ep_irq_thread, IRQF_SHARED | IRQF_ONESHOT, + "tegra264-pcie-ep-intr", (void *)pcie); + if (ret) { + dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret); + goto fail_get_irq; + } + + return 0; + +fail_get_irq: + tegra_bpmp_put(pcie->bpmp); +fail_get_bpmp: + pci_epc_mem_exit(pcie->epc); + + return ret; +} + +static const struct of_device_id tegra264_pcie_ep_of_match[] = { + { + .compatible = "nvidia,tegra264-pcie-ep", + }, + {}, +}; + +static int tegra264_pcie_ep_remove(struct platform_device *pdev) +{ + struct tegra264_pcie_ep *pcie = platform_get_drvdata(pdev); + + pci_epc_mem_exit(pcie->epc); + tegra_bpmp_put(pcie->bpmp); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra264_pcie_ep_suspend(struct device *dev) +{ + struct tegra264_pcie_ep *pcie = dev_get_drvdata(dev); + + if (pcie->ep_state == EP_STATE_ENABLED) { + dev_err(dev, "Suspend is not allowed when PCIe EP is initialized\n"); + return -EPERM; + } + + disable_irq(pcie->pex_rst_irq); + + return 0; +} + +static int tegra264_pcie_ep_resume(struct device *dev) +{ + struct tegra264_pcie_ep *pcie = dev_get_drvdata(dev); + + enable_irq(pcie->pex_rst_irq); + + return 0; +} + +static const struct dev_pm_ops tegra264_pcie_ep_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tegra264_pcie_ep_suspend, tegra264_pcie_ep_resume) +}; +#endif + +static struct platform_driver tegra264_pcie_ep_driver = { + .probe = tegra264_pcie_ep_probe, + .remove = tegra264_pcie_ep_remove, + .driver = { + .name = "tegra264-pcie-ep", + .of_match_table = tegra264_pcie_ep_of_match, +#ifdef CONFIG_PM_SLEEP + .pm = &tegra264_pcie_ep_dev_pm_ops, +#endif + }, +}; + +module_platform_driver(tegra264_pcie_ep_driver); + +MODULE_DEVICE_TABLE(of, tegra264_pcie_ep_of_match); + +MODULE_AUTHOR("Manikanta Maddireddy "); +MODULE_DESCRIPTION("NVIDIA Tegra264 PCIe EP controller driver"); +MODULE_LICENSE("GPL v2");