lkml.org 
[lkml]   [2022]   [Nov]   [21]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH 3/6] PCI: dwc: fsd: Add FSD PCIe Controller driver support
Date
Add PCIe controller driver file for PCIe controller
found in fsd SoC family. This driver adds support for both RC
and EP mode.

Signed-off-by: Niyas Ahmed S T <niyas.ahmed@samsung.com>
Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
Signed-off-by: Padmanabhan Rajanbabu <p.rajanbabu@samsung.com>
Signed-off-by: Shradha Todi <shradha.t@samsung.com>
---
drivers/pci/controller/dwc/Kconfig | 35 +
drivers/pci/controller/dwc/Makefile | 1 +
drivers/pci/controller/dwc/pcie-fsd.c | 1021 +++++++++++++++++++++++++
3 files changed, 1057 insertions(+)
create mode 100644 drivers/pci/controller/dwc/pcie-fsd.c

diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
index 62ce3abf0f19..9a3d194c979f 100644
--- a/drivers/pci/controller/dwc/Kconfig
+++ b/drivers/pci/controller/dwc/Kconfig
@@ -14,6 +14,41 @@ config PCIE_DW_EP
bool
select PCIE_DW

+config PCIE_FSD
+ bool "Samsung FSD PCIe Controller"
+ default n
+ help
+ Enables support for the PCIe controller in the FSD SoC. There are
+ total three instances of PCIe controller in FSD. This controller
+ can work either in RC or EP mode. In order to enable host-specific
+ features, PCI_FSD_HOST must be selected and in order to enable
+ device-specific feature PCI_FSD_EP must be selected.
+
+config PCIE_FSD_HOST
+ bool "PCIe FSD Host Mode"
+ depends on PCI
+ depends on PCI_MSI_IRQ_DOMAIN || PCI_DOMAIN
+ select PCIE_DW_HOST
+ select PCIE_FSD
+ default n
+ help
+ Enables support for the PCIe controller in the FSD SoC to work in
+ host (RC) mode. In order to enable host-specific features,
+ PCIE_DW_HOST must be selected. PCIE_FSD should be selected for
+ fsd controller specific settings.
+
+config PCIE_FSD_EP
+ bool "PCIe FSD Endpoint Mode"
+ depends on PCI_ENDPOINT
+ select PCIE_DW_EP
+ select PCIE_FSD
+ default n
+ help
+ Enables support for the PCIe controller in the FSD SoC to work in
+ endpoint mode. In order to enable device-specific feature
+ PCI_FSD_EP must be selected. PCIE_FSD should be selected for
+ fsd controller specific settings.
+
config PCI_DRA7XX
tristate

diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile
index 8ba7b67f5e50..b76fa6b4e79f 100644
--- a/drivers/pci/controller/dwc/Makefile
+++ b/drivers/pci/controller/dwc/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_PCIE_TEGRA194) += pcie-tegra194.o
obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o
obj-$(CONFIG_PCIE_UNIPHIER_EP) += pcie-uniphier-ep.o
obj-$(CONFIG_PCIE_VISCONTI_HOST) += pcie-visconti.o
+obj-$(CONFIG_PCIE_FSD) += pcie-fsd.o

# The following drivers are for devices that use the generic ACPI
# pci_root.c driver but don't support standard ECAM config access.
diff --git a/drivers/pci/controller/dwc/pcie-fsd.c b/drivers/pci/controller/dwc/pcie-fsd.c
new file mode 100644
index 000000000000..4531efbfc313
--- /dev/null
+++ b/drivers/pci/controller/dwc/pcie-fsd.c
@@ -0,0 +1,1021 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe host controller driver for Tesla fsd SoC
+ *
+ * Copyright (C) 2017-2022 Samsung Electronics Co., Ltd. http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pci.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/resource.h>
+#include <linux/mfd/syscon.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+#define to_fsd_pcie(x) dev_get_drvdata((x)->dev)
+
+/* PCIe ELBI registers */
+#define PCIE_APP_LTSSM_ENABLE 0x054
+#define PCIE_ELBI_LTSSM_ENABLE 0x1
+#define PCIE_ELBI_LTSSM_DISABLE 0x0
+#define PCIE_ELBI_CXPL_DEBUG_00_31 0x2C8
+#define LTSSM_STATE_MASK 0x3f
+#define LTSSM_STATE_L0 0x11
+#define PCIE_FSD_DEVICE_TYPE 0x080
+#define DEVICE_TYPE_RC 0x4
+#define DEVICE_TYPE_EP 0x0
+#define IRQ_MSI_ENABLE BIT(17)
+#define IRQ0_EN 0x10
+#define IRQ1_EN 0x14
+#define IRQ2_EN 0x18
+#define IRQ5_EN 0x1c
+#define IRQ0_STS 0x0
+#define IRQ1_STS 0x4
+#define IRQ2_STS 0x8
+#define IRQ5_STS 0xc
+
+/* Gen3 Control Register */
+#define PCIE_GEN3_RELATED_OFF 0x890
+/* Parameters for equalization feature */
+#define PCIE_GEN3_EQUALIZATION_DISABLE BIT(16)
+#define PCIE_GEN3_EQ_PHASE_2_3 BIT(9)
+#define PCIE_GEN3_RXEQ_PH01_EN BIT(12)
+#define PCIE_GEN3_RXEQ_RGRDLESS_RXTS BIT(13)
+
+/**
+ * struct fsd_pcie - representation of the pci controller
+ * @pci: representation of dwc pcie device structure
+ * @aux_clk: auxiliary clock for the pci block
+ * @dbi_clk: DBI clock
+ * @mstr_clk: master clock
+ * @slv_clk: slave clock
+ * @pdata: private data to determine the oprations supported by device
+ * @appl_base: represent the appl base
+ * @sysreg: represent the system register base
+ * @sysreg_base: represents the offset of the system register required
+ * @phy: represents the phy device associated for the controller
+ */
+struct fsd_pcie {
+ struct dw_pcie *pci;
+ struct clk *aux_clk;
+ struct clk *dbi_clk;
+ struct clk *mstr_clk;
+ struct clk *slv_clk;
+ const struct fsd_pcie_pdata *pdata;
+ void __iomem *appl_base;
+ struct regmap *sysreg;
+ unsigned int sysreg_base;
+ struct phy *phy;
+};
+
+enum fsd_pcie_addr_type {
+ ADDR_TYPE_DBI = 0x0,
+ ADDR_TYPE_DBI2 = 0x32,
+ ADDR_TYPE_ATU = 0x36,
+ ADDR_TYPE_DMA = 0x37,
+};
+
+enum IRQ0_ERR_BITS {
+ APP_PARITY_ERRS_0,
+ APP_PARITY_ERRS_1,
+ APP_PARITY_ERRS_2,
+ CFG_BW_MGT_INT = 4,
+ CFG_LINK_AUTO_BW_INT,
+ CFG_SYS_ERR_RC = 7,
+ DPA_SUBSTATE_UPDATE,
+ FLUSH_DONE,
+ RADM_CORRECTABLE_ERR = 12,
+ RADM_FATAL_ERR,
+ RADM_MSG_CPU_ACTIVE = 22,
+ RADM_MSG_IDLE,
+ RADM_MSG_LTR,
+ RADM_MSG_OBFF,
+ RADM_MSG_UNLOCK,
+ RADM_NONFATAL_ERR,
+ RADM_PM_PME,
+ RADM_PM_TO_ACK,
+ RADM_PM_TURNOFF,
+ RADM_VENDOR_MSG,
+};
+
+enum IRQ1_ERR_BITS {
+ TRGT_CPL_TIMEOUT = 0,
+ VEN_MSG_GRANT,
+ VEN_MSI_GRANT,
+};
+
+enum IRQ2_ERR_BITS {
+ APP_LTR_MSG_GRANT = 0,
+ APP_OBFF_MSG_GRANT,
+ CFG_AER_RC_ERR_INT,
+ CFG_BUS_MASTER_EN,
+ CFG_LINK_EQ_REQ_INT,
+ CFG_PME_INT,
+ EDMA_INT_0,
+ EDMA_INT_1,
+ EDMA_INT_2,
+ EDMA_INT_3,
+ EDMA_INT_4,
+ EDMA_INT_5,
+ EDMA_INT_6,
+ EDMA_INT_7,
+ PM_LINKST_IN_L0S = 18,
+ PM_LINKST_IN_L1,
+ PM_LINKST_IN_L1SUB_0,
+ PM_LINKST_IN_L2,
+ PM_LINKST_L2_EXIT,
+ PM_XTLH_BLOCK_TLP,
+ RADM_CPL_TIMEOUT,
+ RADM_Q_NOT_EMPTY,
+ RDLH_LINK_UP_0,
+ SMLH_LINK_UP = 29,
+ WAKE,
+ COMPARE_END_CHECKER,
+};
+
+enum IRQ5_ERR_BITS {
+ LINK_REQ_RST_NOT,
+ PM_LINKST_IN_L1SUB_1,
+ RDLH_LINK_UP_1,
+ SMLH_REQ_RST_NOT,
+};
+
+struct fsd_pcie_res_ops {
+ int (*get_mem_resources)(struct platform_device *pdev,
+ struct fsd_pcie *fsd_ctrl);
+ int (*get_clk_resources)(struct platform_device *pdev,
+ struct fsd_pcie *fsd_ctrl);
+ int (*init_clk_resources)(struct fsd_pcie *fsd_ctrl);
+ void (*deinit_clk_resources)(struct fsd_pcie *fsd_ctrl);
+};
+
+struct fsd_pcie_irq {
+ irqreturn_t (*pcie_msi_irq_handler)(int irq, void *arg);
+ void (*pcie_msi_init)(struct fsd_pcie *fsd_ctrl);
+ irqreturn_t (*pcie_sub_ctrl_handler)(int irq, void *arg);
+};
+
+struct fsd_pcie_pdata {
+ const struct dw_pcie_ops *dwc_ops;
+ struct dw_pcie_host_ops *host_ops;
+ const struct fsd_pcie_res_ops *res_ops;
+ const struct fsd_pcie_irq *irq_data;
+ unsigned int appl_cxpl_debug_00_31;
+ int op_mode;
+};
+
+static int fsd_pcie_get_mem_resources(struct platform_device *pdev,
+ struct fsd_pcie *fsd_ctrl)
+{
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ /* External Local Bus interface(ELBI) Register */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "appl");
+ if (!res)
+ return -EINVAL;
+ fsd_ctrl->appl_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fsd_ctrl->appl_base)) {
+ dev_err(dev, "Failed to map appl_base\n");
+ return PTR_ERR(fsd_ctrl->appl_base);
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
+ if (!res)
+ return -EINVAL;
+ fsd_ctrl->pci->dbi_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(fsd_ctrl->pci->dbi_base)) {
+ dev_err(dev, "failed to map dbi_base\n");
+ return PTR_ERR(fsd_ctrl->pci->dbi_base);
+ }
+
+ /* sysreg regmap handle */
+ fsd_ctrl->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "tesla,pcie-sysreg");
+ if (IS_ERR(fsd_ctrl->sysreg)) {
+ dev_err(dev, "Sysreg regmap lookup failed.\n");
+ return PTR_ERR(fsd_ctrl->sysreg);
+ }
+
+ ret = of_property_read_u32_index(dev->of_node, "tesla,pcie-sysreg", 1,
+ &fsd_ctrl->sysreg_base);
+ if (ret) {
+ dev_err(dev, "Couldn't get the register offset for syscon!\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fsd_pcie_get_clk_resources(struct platform_device *pdev,
+ struct fsd_pcie *fsd_ctrl)
+{
+ struct device *dev = &pdev->dev;
+
+ fsd_ctrl->aux_clk = devm_clk_get(dev, "aux_clk");
+ if (IS_ERR(fsd_ctrl->aux_clk)) {
+ dev_err(dev, "couldn't get aux clock\n");
+ return -EINVAL;
+ }
+
+ fsd_ctrl->dbi_clk = devm_clk_get(dev, "dbi_clk");
+ if (IS_ERR(fsd_ctrl->dbi_clk)) {
+ dev_err(dev, "couldn't get dbi clk\n");
+ return -EINVAL;
+ }
+
+ fsd_ctrl->slv_clk = devm_clk_get(dev, "slv_clk");
+ if (IS_ERR(fsd_ctrl->slv_clk)) {
+ dev_err(dev, "couldn't get slave clock\n");
+ return -EINVAL;
+ }
+
+ fsd_ctrl->mstr_clk = devm_clk_get(dev, "mstr_clk");
+ if (IS_ERR(fsd_ctrl->mstr_clk)) {
+ dev_err(dev, "couldn't get master clk\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fsd_pcie_init_clk_resources(struct fsd_pcie *fsd_ctrl)
+{
+ clk_prepare_enable(fsd_ctrl->aux_clk);
+ clk_prepare_enable(fsd_ctrl->dbi_clk);
+ clk_prepare_enable(fsd_ctrl->mstr_clk);
+ clk_prepare_enable(fsd_ctrl->slv_clk);
+
+ return 0;
+}
+
+static void fsd_pcie_deinit_clk_resources(struct fsd_pcie *fsd_ctrl)
+{
+ clk_disable_unprepare(fsd_ctrl->slv_clk);
+ clk_disable_unprepare(fsd_ctrl->mstr_clk);
+ clk_disable_unprepare(fsd_ctrl->dbi_clk);
+ clk_disable_unprepare(fsd_ctrl->aux_clk);
+}
+
+static const struct fsd_pcie_res_ops fsd_pcie_res_ops_data = {
+ .get_mem_resources = fsd_pcie_get_mem_resources,
+ .get_clk_resources = fsd_pcie_get_clk_resources,
+ .init_clk_resources = fsd_pcie_init_clk_resources,
+ .deinit_clk_resources = fsd_pcie_deinit_clk_resources,
+};
+
+static void fsd_pcie_stop_link(struct dw_pcie *pci)
+{
+ u32 reg;
+ struct fsd_pcie *fsd_ctrl = to_fsd_pcie(pci);
+
+ reg = readl(fsd_ctrl->appl_base + PCIE_APP_LTSSM_ENABLE);
+ reg &= ~PCIE_ELBI_LTSSM_ENABLE;
+ writel(reg, fsd_ctrl->appl_base + PCIE_APP_LTSSM_ENABLE);
+}
+
+static int fsd_pcie_establish_link(struct dw_pcie *pci)
+{
+ struct device *dev = pci->dev;
+ struct fsd_pcie *fsd_ctrl = to_fsd_pcie(pci);
+ struct dw_pcie_ep *ep;
+
+ if (dw_pcie_link_up(pci)) {
+ dev_info(dev, "Link already up\n");
+ return 0;
+ }
+
+ /* assert LTSSM enable */
+ writel(PCIE_ELBI_LTSSM_ENABLE, fsd_ctrl->appl_base +
+ PCIE_APP_LTSSM_ENABLE);
+
+ /* check if the link is up or not */
+ if (!dw_pcie_wait_for_link(pci)) {
+ dev_info(dev, "Link up done successfully\n");
+ if (fsd_ctrl->pdata->op_mode == DEVICE_TYPE_EP) {
+ ep = &pci->ep;
+ dw_pcie_ep_linkup(ep);
+ }
+ return 0;
+ }
+
+ if (fsd_ctrl->pdata->op_mode == DEVICE_TYPE_RC) {
+ /* Return success as link might come up later */
+ return 0;
+ }
+
+ return -ETIMEDOUT;
+}
+
+static void handle_irq0_interrupts(u32 val, u32 is_en)
+{
+ u32 bit_off = 0;
+
+ if (val) {
+ while (bit_off < 32) {
+ if ((val & (0x1 << bit_off)) == 0 || (is_en &
+ (0x1 << bit_off)) == 0) {
+ bit_off++;
+ continue;
+ }
+ switch (bit_off) {
+ case RADM_VENDOR_MSG:
+ pr_info("Interrupt received for\n");
+ break;
+ case RADM_PM_TURNOFF:
+ pr_info("Interrupt received for RADM_PM_TURNOFF\n");
+ break;
+ case RADM_PM_TO_ACK:
+ pr_info("Interrupt received for RADM_PM_TO_ACK\n");
+ break;
+ case RADM_PM_PME:
+ pr_info("Interrupt received for RADM_PM_PME\n");
+ break;
+ case RADM_NONFATAL_ERR:
+ pr_info("Interrupt received for RADM_NONFATAL_ERR\n");
+ break;
+ case RADM_MSG_UNLOCK:
+ pr_info("Interrupt received for RADM_MSG_UNLOCK\n");
+ break;
+ case RADM_MSG_OBFF:
+ pr_info("Interrupt received for RADM_MSG_OBFF\n");
+ break;
+ case RADM_MSG_LTR:
+ pr_info("Interrupt received for RADM_MSG_LTR\n");
+ break;
+ case RADM_MSG_IDLE:
+ pr_info("Interrupt received for RADM_MSG_IDLE\n");
+ break;
+ case RADM_MSG_CPU_ACTIVE:
+ pr_info("Interrupt received for RADM_MSG_CPU_ACTIVE\n");
+ break;
+ case RADM_FATAL_ERR:
+ pr_info("Interrupt received for RADM_FATAL_ERR\n");
+ break;
+ case RADM_CORRECTABLE_ERR:
+ pr_info("Interrupt received for RADM_CORRECTABLE_ERR\n");
+ break;
+ case FLUSH_DONE:
+ pr_info("Interrupt received for FLUSH_DONE\n");
+ break;
+ case DPA_SUBSTATE_UPDATE:
+ pr_info("Interrupt received for DPA_SUBSTATE_UPDATE\n");
+ break;
+ case CFG_SYS_ERR_RC:
+ pr_info("Interrupt received for CFG_SYS_ERR_RC\n");
+ break;
+ case CFG_LINK_AUTO_BW_INT:
+ pr_info("Interrupt received for CFG_LINK_AUTO_BW_INT\n");
+ break;
+ case CFG_BW_MGT_INT:
+ pr_info("Interrupt received for CFG_BW_MGT_INT\n");
+ break;
+ case APP_PARITY_ERRS_2:
+ pr_info("Interrupt received for APP_PARITY_ERRS_2\n");
+ break;
+ case APP_PARITY_ERRS_1:
+ pr_info("Interrupt received for APP_PARITY_ERRS_1\n");
+ break;
+ case APP_PARITY_ERRS_0:
+ pr_info("Interrupt received for APP_PARITY_ERRS_0\n");
+ break;
+ default:
+ pr_info("Unknown Interrupt in IRQ0[%d]\n", bit_off);
+ break;
+ }
+ bit_off++;
+ }
+ }
+}
+
+static void handle_irq1_interrupts(u32 val, u32 is_en)
+{
+ u32 bit_off = 0;
+
+ if (val) {
+ while (bit_off < 32) {
+ if ((val & (0x1 << bit_off)) == 0 || (is_en &
+ (0x1 << bit_off)) == 0) {
+ bit_off++;
+ continue;
+ }
+ switch (bit_off) {
+ case TRGT_CPL_TIMEOUT:
+ pr_info("Interrupt for TRGT_CPL_TIMEOUT\n");
+ break;
+ case VEN_MSG_GRANT:
+ pr_info("Interrupt for VEN_MSG_GRANT\n");
+ break;
+ case VEN_MSI_GRANT:
+ pr_info("Interrupt for VEN_MSI_GRANT\n");
+ break;
+ default:
+ pr_info("Unknown Interrupt in IRQ1[%d]\n", bit_off);
+ break;
+ }
+ bit_off++;
+ }
+ }
+}
+
+static void handle_irq2_interrupts(u32 val, u32 is_en)
+{
+ u32 bit_off = 0;
+
+ if (val) {
+ while (bit_off < 32) {
+ if ((val & (0x1 << bit_off)) == 0 || (is_en &
+ (0x1 << bit_off)) == 0) {
+ bit_off++;
+ continue;
+ }
+ switch (bit_off) {
+ /* To indicate that controller has accepted to send
+ * Latency Tolerance reporting message
+ */
+ case APP_LTR_MSG_GRANT:
+ pr_info("Interrupt for APP_LTR_MSG_GRANT\n");
+ break;
+ case APP_OBFF_MSG_GRANT:
+ pr_info("Interrupt for APP_OBFF_MSG_GRANT\n");
+ break;
+ case CFG_AER_RC_ERR_INT:
+ pr_info("Interrupt for CFG_AER_RC_ERR_INT\n");
+ break;
+ /* IRQ when bus master is enabled */
+ case CFG_BUS_MASTER_EN:
+ pr_info("Interrupt for CFG_BUS_MASTER_EN\n");
+ break;
+ /* IRQ to indicate that link Equalization request has been set */
+ case CFG_LINK_EQ_REQ_INT:
+ pr_info("Interrupt for CFG_LINK_EQ_REQ_INT\n");
+ break;
+ case CFG_PME_INT:
+ pr_info("Interrupt for CFG_PME_INIT\n");
+ break;
+ case EDMA_INT_0:
+ case EDMA_INT_1:
+ case EDMA_INT_2:
+ case EDMA_INT_3:
+ case EDMA_INT_4:
+ case EDMA_INT_5:
+ case EDMA_INT_6:
+ case EDMA_INT_7:
+ pr_info("Interrupt for DMA\n");
+ break;
+ /* IRQ when link entres L0s */
+ case PM_LINKST_IN_L0S:
+ pr_info("Interrupt for PM_LINKST_IN_L0S\n");
+ break;
+ /* IRQ when link enters L1 */
+ case PM_LINKST_IN_L1:
+ pr_info("Interrupt for PM_LINKST_IN_L1\n");
+ break;
+ /* IRQ when link enters L1 substate */
+ case PM_LINKST_IN_L1SUB_0:
+ pr_info("Interrupt for PM_LINKST_IN_L1SUB_0\n");
+ break;
+ /* IRQ when link enters L2 */
+ case PM_LINKST_IN_L2:
+ pr_info("Interrupt for PM_LINKST_IN_L2\n");
+ break;
+ /* IRQ when link exits L2 */
+ case PM_LINKST_L2_EXIT:
+ pr_info("Interrupt for PM_LINKST_L2_EXIT\n");
+ break;
+ /* Indicates that application must stop sending new
+ * outbound TLP requests due to current power state
+ */
+ case PM_XTLH_BLOCK_TLP:
+ pr_info("Interrupt for PM_XTLH_BLOCK_TLP\n");
+ break;
+ /* Request failed to complete in time */
+ case RADM_CPL_TIMEOUT:
+ pr_info("Interrupt for RADM_CPL_TIMEOUT\n");
+ break;
+ /* Level indicating that receive queues contain TLP header/data */
+ case RADM_Q_NOT_EMPTY:
+ pr_info("Interrupt for RADM_Q_NOT_EMPTY\n");
+ break;
+ /* Data link layer up/down indicator */
+ case RDLH_LINK_UP_0:
+ pr_info("Interrupt for RDLH_LINK_UP_0\n");
+ break;
+ /* Phy link up/down indicator */
+ case SMLH_LINK_UP:
+ pr_info("Interrupt for SMLH_LINK_UP\n");
+ break;
+ case WAKE:
+ pr_info("Interrupt for WAKE\n");
+ break;
+ case COMPARE_END_CHECKER:
+ pr_info("Interrupt for COMPARE_END_CHECKER\n");
+ break;
+ default:
+ pr_info("Unknown Interrupt in IRQ2[%d]\n", bit_off);
+ break;
+ }
+ bit_off++;
+ }
+ }
+}
+
+static void handle_irq5_interrupts(u32 val, u32 is_en)
+{
+ u32 bit_off = 0;
+
+ if (val) {
+ while (bit_off < 32) {
+ if ((val & (0x1 << bit_off)) == 0 || (is_en &
+ (0x1 << bit_off)) == 0) {
+ bit_off++;
+ continue;
+ }
+ switch (bit_off) {
+ case LINK_REQ_RST_NOT:
+ pr_info("Interrupt for LINK_REQ_RST_NOT\n");
+ break;
+ case PM_LINKST_IN_L1SUB_1:
+ pr_info("Interrupt for L1 SUB state Exit\n");
+ break;
+ case RDLH_LINK_UP_1:
+ pr_info("Interrupt for RDLH_LINK_UP_1\n");
+ break;
+ /* Reset request because PHY link went down/ or got hot reset */
+ case SMLH_REQ_RST_NOT:
+ pr_info("Interrupt for SMLH_REQ_RST_NOT\n");
+ break;
+ default:
+ pr_info("Unknown Interrupt in IRQ5[%d]\n", bit_off);
+ break;
+ }
+ bit_off++;
+ }
+ }
+}
+
+/*
+ * fsd_pcie_sub_ctrl_handler : Interrupt handler for all PCIe interrupts.
+ *
+ * These interrupts trigger on different events happening in the PCIe
+ * controller like link status, link entering and exiting low power
+ * states like L0s, L1, DMA completion/abort interrupts, wake being
+ * triggered and other information.
+ *
+ * IRQ_0: (offset 0x0): IRQ for pulse output 1
+ * Enable these interrupts at offset 0x10
+ * IRQ_1: (offset 0x4): IRQ for pulse output 2
+ * Enable these interrupts at offset 0x14
+ * IRQ_2: (offset 0x8): IRQ for level output, rising edge
+ * Enable these interrupts at offset 0x18
+ * IRQ_5: (offset 0xC): IRQ for level output, falling edge
+ * Enable these interrupts at offset 0x1C
+ */
+
+static irqreturn_t fsd_pcie_sub_ctrl_handler(int irq, void *arg)
+{
+ u32 irq0, irq1, irq2, irq5;
+ struct fsd_pcie *fsd_ctrl = arg;
+ u32 irq0_en, irq1_en, irq2_en, irq5_en;
+
+ /* Read IRQ0 status */
+ irq0 = readl(fsd_ctrl->appl_base + IRQ0_STS);
+ /* Clear IRQ0 status after storing status value */
+ writel(irq0, fsd_ctrl->appl_base + IRQ0_STS);
+
+ /* Read IRQ1 status */
+ irq1 = readl(fsd_ctrl->appl_base + IRQ1_STS);
+ /* Clear IRQ1 status after storing status value */
+ writel(irq1, fsd_ctrl->appl_base + IRQ1_STS);
+
+ /* Read IRQ2 status */
+ irq2 = readl(fsd_ctrl->appl_base + IRQ2_STS);
+ /* Clear IRQ2 status after storing status value */
+ writel(irq2, fsd_ctrl->appl_base + IRQ2_STS);
+
+ /* Read IRQ5 status */
+ irq5 = readl(fsd_ctrl->appl_base + IRQ5_STS);
+ /* Clear IRQ5 status after storing status value */
+ writel(irq5, fsd_ctrl->appl_base + IRQ5_STS);
+
+ irq0_en = readl(fsd_ctrl->appl_base + IRQ0_EN);
+ irq1_en = readl(fsd_ctrl->appl_base + IRQ1_EN);
+ irq2_en = readl(fsd_ctrl->appl_base + IRQ2_EN);
+ irq5_en = readl(fsd_ctrl->appl_base + IRQ5_EN);
+ /* Handle all interrupts */
+ handle_irq0_interrupts(irq0, irq0_en);
+ handle_irq1_interrupts(irq1, irq1_en);
+ handle_irq2_interrupts(irq2, irq2_en);
+ handle_irq5_interrupts(irq5, irq5_en);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fsd_pcie_msi_irq_handler(int irq, void *arg)
+{
+ u32 val;
+ struct fsd_pcie *fsd_ctrl = arg;
+ struct dw_pcie *pci = fsd_ctrl->pci;
+ struct dw_pcie_rp *pp = &pci->pp;
+
+ val = readl(fsd_ctrl->appl_base + IRQ2_STS);
+
+ if ((val & IRQ_MSI_ENABLE) == IRQ_MSI_ENABLE) {
+ val &= IRQ_MSI_ENABLE;
+ writel(val, fsd_ctrl->appl_base + IRQ2_STS);
+ dw_handle_msi_irq(pp);
+ } else {
+ fsd_pcie_sub_ctrl_handler(irq, arg);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void fsd_pcie_msi_init(struct fsd_pcie *fsd_ctrl)
+{
+ int val;
+
+ /* enable MSI interrupt */
+ val = readl(fsd_ctrl->appl_base + IRQ2_EN);
+ val |= IRQ_MSI_ENABLE;
+ writel(val, fsd_ctrl->appl_base + IRQ2_EN);
+}
+
+static void fsd_pcie_enable_interrupts(struct fsd_pcie *fsd_ctrl)
+{
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ fsd_ctrl->pdata->irq_data->pcie_msi_init(fsd_ctrl);
+}
+
+static u32 fsd_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base,
+ u32 reg, size_t size)
+{
+ bool is_atu = false;
+ struct fsd_pcie *fsd_ctrl = to_fsd_pcie(pci);
+ u32 val;
+
+ if (pci->atu_base) {
+ if (base >= pci->atu_base) {
+
+ is_atu = true;
+ regmap_write(fsd_ctrl->sysreg, fsd_ctrl->sysreg_base,
+ ADDR_TYPE_ATU);
+ base = base - DEFAULT_DBI_ATU_OFFSET;
+ }
+ }
+
+ dw_pcie_read(base + reg, size, &val);
+
+ if (is_atu)
+ regmap_write(fsd_ctrl->sysreg, fsd_ctrl->sysreg_base, ADDR_TYPE_DBI);
+
+ return val;
+}
+
+static void fsd_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base,
+ u32 reg, size_t size, u32 val)
+{
+ struct fsd_pcie *fsd_ctrl = to_fsd_pcie(pci);
+ bool is_atu = false;
+
+ if (pci->atu_base) {
+ if (base >= pci->atu_base) {
+ is_atu = true;
+ regmap_write(fsd_ctrl->sysreg, fsd_ctrl->sysreg_base,
+ ADDR_TYPE_ATU);
+ base = base - DEFAULT_DBI_ATU_OFFSET;
+ }
+ }
+
+ dw_pcie_write(base + reg, size, val);
+
+ if (is_atu)
+ regmap_write(fsd_ctrl->sysreg, fsd_ctrl->sysreg_base, ADDR_TYPE_DBI);
+}
+
+static void fsd_pcie_write_dbi2(struct dw_pcie *pci, void __iomem *base,
+ u32 reg, size_t size, u32 val)
+{
+ struct fsd_pcie *fsd_ctrl = to_fsd_pcie(pci);
+
+ regmap_write(fsd_ctrl->sysreg, fsd_ctrl->sysreg_base, ADDR_TYPE_DBI2);
+ dw_pcie_write(pci->dbi_base + reg, size, val);
+ regmap_write(fsd_ctrl->sysreg, fsd_ctrl->sysreg_base, ADDR_TYPE_DBI);
+}
+
+static int fsd_pcie_link_up(struct dw_pcie *pci)
+{
+ u32 val;
+ struct fsd_pcie *fsd_ctrl = to_fsd_pcie(pci);
+
+ val = readl(fsd_ctrl->appl_base +
+ fsd_ctrl->pdata->appl_cxpl_debug_00_31);
+
+ return (val & LTSSM_STATE_MASK) == LTSSM_STATE_L0;
+}
+
+static int fsd_pcie_host_init(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct fsd_pcie *fsd_ctrl = to_fsd_pcie(pci);
+
+ dw_pcie_writel_dbi(pci, PCIE_GEN3_RELATED_OFF,
+ (PCIE_GEN3_EQ_PHASE_2_3 |
+ PCIE_GEN3_RXEQ_PH01_EN |
+ PCIE_GEN3_RXEQ_RGRDLESS_RXTS));
+
+ fsd_pcie_enable_interrupts(fsd_ctrl);
+
+ return 0;
+}
+
+static struct dw_pcie_host_ops fsd_pcie_host_ops = {
+ .host_init = fsd_pcie_host_init,
+};
+
+static int fsd_pcie_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);
+
+ switch (type) {
+ case PCI_EPC_IRQ_LEGACY:
+ dev_err(pci->dev, "EP does not support legacy IRQs\n");
+ return -EINVAL;
+ case PCI_EPC_IRQ_MSI:
+ return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
+ default:
+ dev_err(pci->dev, "UNKNOWN IRQ type\n");
+ }
+
+ return 0;
+}
+
+static const struct pci_epc_features fsd_pcie_epc_features = {
+ .linkup_notifier = false,
+ .msi_capable = true,
+ .msix_capable = false,
+};
+
+static const struct pci_epc_features*
+ fsd_pcie_get_features(struct dw_pcie_ep *ep)
+{
+ return &fsd_pcie_epc_features;
+}
+
+static struct dw_pcie_ep_ops fsd_dw_pcie_ep_ops = {
+ .raise_irq = fsd_pcie_raise_irq,
+ .get_features = fsd_pcie_get_features,
+};
+
+static const struct fsd_pcie_irq fsd_pcie_irq_data = {
+ .pcie_msi_irq_handler = fsd_pcie_msi_irq_handler,
+ .pcie_msi_init = fsd_pcie_msi_init,
+ .pcie_sub_ctrl_handler = fsd_pcie_sub_ctrl_handler,
+};
+
+static int __init fsd_add_pcie_ep(struct fsd_pcie *fsd_ctrl,
+ struct platform_device *pdev)
+{
+ struct dw_pcie_ep *ep;
+ struct dw_pcie *pci = fsd_ctrl->pci;
+ int ret;
+ struct device *dev = &pdev->dev;
+
+ ep = &pci->ep;
+ ep->ops = &fsd_dw_pcie_ep_ops;
+
+ dw_pcie_writel_dbi(pci, PCIE_GEN3_RELATED_OFF,
+ (PCIE_GEN3_EQUALIZATION_DISABLE |
+ PCIE_GEN3_RXEQ_PH01_EN |
+ PCIE_GEN3_RXEQ_RGRDLESS_RXTS));
+
+ ret = dw_pcie_ep_init(ep);
+ if (ret)
+ dev_err(dev, "failed to initialize endpoint\n");
+
+ return ret;
+}
+
+static int __init fsd_add_pcie_port(struct fsd_pcie *fsd_ctrl,
+ struct platform_device *pdev)
+{
+ int irq;
+ struct device *dev = &pdev->dev;
+ int irq_flags;
+ int ret;
+ struct dw_pcie *pci = fsd_ctrl->pci;
+ struct dw_pcie_rp *pp = &pci->pp;
+
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ irq = platform_get_irq_byname(pdev, "msi");
+ if (!irq) {
+ dev_err(dev, "failed to get msi irq\n");
+ return -ENODEV;
+ }
+
+ irq_flags = IRQF_TRIGGER_RISING | IRQF_SHARED | IRQF_NO_THREAD;
+
+ ret = devm_request_irq(dev, irq,
+ fsd_ctrl->pdata->irq_data->pcie_msi_irq_handler,
+ irq_flags, "fsd-pcie", fsd_ctrl);
+ if (ret) {
+ dev_err(dev, "failed to request msi irq\n");
+ return ret;
+ }
+ pp->msi_irq[0] = -ENODEV;
+ }
+
+ ret = dw_pcie_host_init(pp);
+ if (ret)
+ dev_err(dev, "failed to initialize host\n");
+
+ return ret;
+}
+
+static const struct dw_pcie_ops fsd_dw_pcie_ops = {
+ .read_dbi = fsd_pcie_read_dbi,
+ .write_dbi = fsd_pcie_write_dbi,
+ .write_dbi2 = fsd_pcie_write_dbi2,
+ .start_link = fsd_pcie_establish_link,
+ .stop_link = fsd_pcie_stop_link,
+ .link_up = fsd_pcie_link_up,
+};
+
+static int fsd_pcie_probe(struct platform_device *pdev)
+{
+ int ret;
+ int irq, irq_flags;
+ struct dw_pcie *pci;
+ struct dw_pcie_rp *pp;
+ struct fsd_pcie *fsd_ctrl;
+ struct device *dev = &pdev->dev;
+ const struct fsd_pcie_pdata *pdata;
+ struct device_node *np = dev->of_node;
+
+ fsd_ctrl = devm_kzalloc(dev, sizeof(*fsd_ctrl), GFP_KERNEL);
+ if (!fsd_ctrl)
+ return -ENOMEM;
+
+ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+
+ pdata = (const struct fsd_pcie_pdata *) of_device_get_match_data(dev);
+
+ fsd_ctrl->pci = pci;
+ fsd_ctrl->pdata = pdata;
+
+ pci->dev = dev;
+ pci->ops = pdata->dwc_ops;
+ pci->dbi_base2 = NULL;
+ pci->dbi_base = NULL;
+ pci->atu_base = NULL;
+ pp = &pci->pp;
+ pp->ops = fsd_ctrl->pdata->host_ops;
+
+ fsd_ctrl->phy = devm_of_phy_get(dev, np, NULL);
+ if (IS_ERR(fsd_ctrl->phy)) {
+ if (PTR_ERR(fsd_ctrl->phy) == -EPROBE_DEFER)
+ return PTR_ERR(fsd_ctrl->phy);
+ }
+
+ phy_init(fsd_ctrl->phy);
+
+ if (pdata->res_ops && pdata->res_ops->get_mem_resources) {
+ ret = pdata->res_ops->get_mem_resources(pdev, fsd_ctrl);
+ if (ret)
+ return ret;
+ }
+
+ if (pdata->res_ops && pdata->res_ops->get_clk_resources) {
+ ret = pdata->res_ops->get_clk_resources(pdev, fsd_ctrl);
+ if (ret)
+ return ret;
+ ret = pdata->res_ops->init_clk_resources(fsd_ctrl);
+ if (ret)
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, fsd_ctrl);
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36));
+ if (ret)
+ goto fail_dma_set;
+
+ switch (fsd_ctrl->pdata->op_mode) {
+ case DEVICE_TYPE_RC:
+ writel(DEVICE_TYPE_RC, fsd_ctrl->appl_base +
+ PCIE_FSD_DEVICE_TYPE);
+ ret = fsd_add_pcie_port(fsd_ctrl, pdev);
+ if (ret)
+ goto fail_add_pcie_port;
+ break;
+ case DEVICE_TYPE_EP:
+ writel(DEVICE_TYPE_EP, fsd_ctrl->appl_base +
+ PCIE_FSD_DEVICE_TYPE);
+
+ ret = fsd_add_pcie_ep(fsd_ctrl, pdev);
+ if (ret)
+ goto fail_add_pcie_ep;
+ break;
+ }
+
+ irq = platform_get_irq_byname(pdev, "sub_ctrl_intr");
+ if (irq > 0) {
+
+ irq_flags = IRQF_TRIGGER_RISING | IRQF_SHARED | IRQF_NO_THREAD;
+
+ ret = devm_request_irq(dev, irq,
+ fsd_ctrl->pdata->irq_data->pcie_sub_ctrl_handler,
+ irq_flags, "fsd-sub-ctrl-pcie", fsd_ctrl);
+ if (ret)
+ dev_err(dev, "failed to request sub ctrl irq\n");
+ }
+
+ dev_info(dev, "FSD PCIe probe completed successfully\n");
+
+ return 0;
+
+fail_dma_set:
+ dev_err(dev, "PCIe Failed to set 36 bit dma mask\n");
+fail_add_pcie_port:
+ phy_exit(fsd_ctrl->phy);
+fail_add_pcie_ep:
+ if (pdata->res_ops && pdata->res_ops->deinit_clk_resources)
+ pdata->res_ops->deinit_clk_resources(fsd_ctrl);
+ return ret;
+}
+
+static int __exit fsd_pcie_remove(struct platform_device *pdev)
+{
+ struct fsd_pcie *fsd_ctrl = platform_get_drvdata(pdev);
+ const struct fsd_pcie_pdata *pdata = fsd_ctrl->pdata;
+
+ if (pdata->res_ops && pdata->res_ops->deinit_clk_resources)
+ pdata->res_ops->deinit_clk_resources(fsd_ctrl);
+
+ return 0;
+}
+
+static const struct fsd_pcie_pdata fsd_pcie_rc_pdata = {
+ .dwc_ops = &fsd_dw_pcie_ops,
+ .host_ops = &fsd_pcie_host_ops,
+ .res_ops = &fsd_pcie_res_ops_data,
+ .irq_data = &fsd_pcie_irq_data,
+ .appl_cxpl_debug_00_31 = PCIE_ELBI_CXPL_DEBUG_00_31,
+ .op_mode = DEVICE_TYPE_RC,
+};
+
+static const struct fsd_pcie_pdata fsd_pcie_ep_pdata = {
+ .dwc_ops = &fsd_dw_pcie_ops,
+ .host_ops = &fsd_pcie_host_ops,
+ .res_ops = &fsd_pcie_res_ops_data,
+ .irq_data = &fsd_pcie_irq_data,
+ .appl_cxpl_debug_00_31 = PCIE_ELBI_CXPL_DEBUG_00_31,
+ .op_mode = DEVICE_TYPE_EP,
+};
+
+static const struct of_device_id fsd_pcie_of_match[] = {
+ {
+ .compatible = "tesla,fsd-pcie",
+ .data = (void *) &fsd_pcie_rc_pdata,
+ },
+ {
+ .compatible = "tesla,fsd-pcie-ep",
+ .data = (void *) &fsd_pcie_ep_pdata,
+ },
+
+ {},
+};
+
+static struct platform_driver fsd_pcie_driver = {
+ .probe = fsd_pcie_probe,
+ .remove = __exit_p(fsd_pcie_remove),
+ .driver = {
+ .name = "fsd-pcie",
+ .of_match_table = fsd_pcie_of_match,
+ },
+};
+
+static int __init fsd_pcie_init(void)
+{
+ return platform_driver_register(&fsd_pcie_driver);
+}
+module_init(fsd_pcie_init);
--
2.17.1
\
 
 \ /
  Last update: 2022-11-21 12:45    [W:0.164 / U:0.576 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site