On Mon, Nov 21, 2022 at 04:22:07PM +0530, Shradha Todi wrote: > 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@xxxxxxxxxxx> > Signed-off-by: Pankaj Dubey <pankaj.dubey@xxxxxxxxxxx> > Signed-off-by: Padmanabhan Rajanbabu <p.rajanbabu@xxxxxxxxxxx> > Signed-off-by: Shradha Todi <shradha.t@xxxxxxxxxxx> > --- > 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> You shouldn't need this header. > +#include <linux/of_device.h> > +#include <linux/of_gpio.h> You shouldn't need this header. > +#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); > +}; Why the indirection? You only have 1 version of all these functions. > + > +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"); The DW core code takes care of this. > + 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"); Print messages on failure, not normal operation. > + 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)) { IIRC, the DW core will do the wait for you. > + 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"); Printing messages is not handling an interrupt. Remove all these. > + 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); You've got the same code twice. Rework this to be a common function that does just: if (ATU) regmap_write(fsd_ctrl->sysreg, fsd_ctrl->sysreg_base, ADDR_TYPE_ATU); else regmap_write(fsd_ctrl->sysreg, fsd_ctrl->sysreg_base, ADDR_TYPE_DBI); Then you just call it followed by dw_pcie_read or dw_pcie_write. > + > + 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"); Wasn't in the binding. And the DWC core does this for you. > + 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); Use the non-DT version. > + 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); Again, get rid of the indirection. If it does vary, don't invent your own ops. We only need 1 level of abstraction. > + 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 > >