diff options
Diffstat (limited to 'drivers/pci/controller/dwc')
| -rw-r--r-- | drivers/pci/controller/dwc/Kconfig | 12 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/Makefile | 1 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pci-imx6.c | 40 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware-debugfs.c | 16 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware-host.c | 107 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware.c | 14 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware.h | 19 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-dw-rockchip.c | 16 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-qcom.c | 327 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-sophgo.c | 257 | 
10 files changed, 674 insertions, 135 deletions
diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index d9f0386396ed..ff6b6d9e18ec 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -19,6 +19,7 @@ config PCIE_DW_DEBUGFS  config PCIE_DW_HOST  	bool  	select PCIE_DW +	select IRQ_MSI_LIB  config PCIE_DW_EP  	bool @@ -296,6 +297,7 @@ config PCIE_QCOM  	select PCIE_DW_HOST  	select CRC8  	select PCIE_QCOM_COMMON +	select PCI_HOST_COMMON  	help  	  Say Y here to enable PCIe controller support on Qualcomm SoCs. The  	  PCIe controller uses the DesignWare core plus Qualcomm-specific @@ -402,6 +404,16 @@ config PCIE_UNIPHIER_EP  	  Say Y here if you want PCIe endpoint controller support on  	  UniPhier SoCs. This driver supports Pro5 SoC. +config PCIE_SOPHGO_DW +	bool "Sophgo DesignWare PCIe controller (host mode)" +	depends on ARCH_SOPHGO || COMPILE_TEST +	depends on PCI_MSI +	depends on OF +	select PCIE_DW_HOST +	help +	  Say Y here if you want PCIe host controller support on +	  Sophgo SoCs. +  config PCIE_SPEAR13XX  	bool "STMicroelectronics SPEAr PCIe controller"  	depends on ARCH_SPEAR13XX || COMPILE_TEST diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index 908cb7f345db..6919d27798d1 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_PCIE_QCOM_EP) += pcie-qcom-ep.o  obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o  obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o  obj-$(CONFIG_PCIE_ROCKCHIP_DW) += pcie-dw-rockchip.o +obj-$(CONFIG_PCIE_SOPHGO_DW) += pcie-sophgo.o  obj-$(CONFIG_PCIE_INTEL_GW) += pcie-intel-gw.o  obj-$(CONFIG_PCIE_KEEMBAY) += pcie-keembay.o  obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 5a38cfaf989b..80e48746bbaf 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -860,7 +860,6 @@ static int imx95_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)  static void imx_pcie_assert_core_reset(struct imx_pcie *imx_pcie)  {  	reset_control_assert(imx_pcie->pciephy_reset); -	reset_control_assert(imx_pcie->apps_reset);  	if (imx_pcie->drvdata->core_reset)  		imx_pcie->drvdata->core_reset(imx_pcie, true); @@ -872,7 +871,6 @@ static void imx_pcie_assert_core_reset(struct imx_pcie *imx_pcie)  static int imx_pcie_deassert_core_reset(struct imx_pcie *imx_pcie)  {  	reset_control_deassert(imx_pcie->pciephy_reset); -	reset_control_deassert(imx_pcie->apps_reset);  	if (imx_pcie->drvdata->core_reset)  		imx_pcie->drvdata->core_reset(imx_pcie, false); @@ -1063,7 +1061,10 @@ static int imx_pcie_add_lut(struct imx_pcie *imx_pcie, u16 rid, u8 sid)  	data1 |= IMX95_PE0_LUT_VLD;  	regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA1, data1); -	data2 = IMX95_PE0_LUT_MASK; /* Match all bits of RID */ +	if (imx_pcie->drvdata->mode == DW_PCIE_EP_TYPE) +		data2 = 0x7; /* In the EP mode, only 'Device ID' is required */ +	else +		data2 = IMX95_PE0_LUT_MASK; /* Match all bits of RID */  	data2 |= FIELD_PREP(IMX95_PE0_LUT_REQID, rid);  	regmap_write(imx_pcie->iomuxc_gpr, IMX95_PE0_LUT_DATA2, data2); @@ -1096,18 +1097,14 @@ static void imx_pcie_remove_lut(struct imx_pcie *imx_pcie, u16 rid)  	}  } -static int imx_pcie_enable_device(struct pci_host_bridge *bridge, -				  struct pci_dev *pdev) +static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid)  { -	struct imx_pcie *imx_pcie = to_imx_pcie(to_dw_pcie_from_pp(bridge->sysdata)); -	u32 sid_i, sid_m, rid = pci_dev_id(pdev); +	struct device *dev = imx_pcie->pci->dev;  	struct device_node *target; -	struct device *dev; +	u32 sid_i, sid_m;  	int err_i, err_m;  	u32 sid = 0; -	dev = imx_pcie->pci->dev; -  	target = NULL;  	err_i = of_map_id(dev->of_node, rid, "iommu-map", "iommu-map-mask",  			  &target, &sid_i); @@ -1182,6 +1179,13 @@ static int imx_pcie_enable_device(struct pci_host_bridge *bridge,  	return imx_pcie_add_lut(imx_pcie, rid, sid);  } +static int imx_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_dev *pdev) +{ +	struct imx_pcie *imx_pcie = to_imx_pcie(to_dw_pcie_from_pp(bridge->sysdata)); + +	return imx_pcie_add_lut_by_rid(imx_pcie, pci_dev_id(pdev)); +} +  static void imx_pcie_disable_device(struct pci_host_bridge *bridge,  				    struct pci_dev *pdev)  { @@ -1247,6 +1251,9 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)  		}  	} +	/* Make sure that PCIe LTSSM is cleared */ +	imx_pcie_ltssm_disable(dev); +  	ret = imx_pcie_deassert_core_reset(imx_pcie);  	if (ret < 0) {  		dev_err(dev, "pcie deassert core reset failed: %d\n", ret); @@ -1385,6 +1392,8 @@ static const struct pci_epc_features imx8m_pcie_epc_features = {  	.msix_capable = false,  	.bar[BAR_1] = { .type = BAR_RESERVED, },  	.bar[BAR_3] = { .type = BAR_RESERVED, }, +	.bar[BAR_4] = { .type = BAR_FIXED, .fixed_size = SZ_256, }, +	.bar[BAR_5] = { .type = BAR_RESERVED, },  	.align = SZ_64K,  }; @@ -1465,9 +1474,6 @@ static int imx_add_pcie_ep(struct imx_pcie *imx_pcie,  	pci_epc_init_notify(ep->epc); -	/* Start LTSSM. */ -	imx_pcie_ltssm_enable(dev); -  	return 0;  } @@ -1764,6 +1770,12 @@ static int imx_pcie_probe(struct platform_device *pdev)  		ret = imx_add_pcie_ep(imx_pcie, pdev);  		if (ret < 0)  			return ret; + +		/* +		 * FIXME: Only single Device (EPF) is supported due to the +		 * Endpoint framework limitation. +		 */ +		imx_pcie_add_lut_by_rid(imx_pcie, 0);  	} else {  		pci->pp.use_atu_msg = true;  		ret = dw_pcie_host_init(&pci->pp); @@ -1912,7 +1924,7 @@ static const struct imx_pcie_drvdata drvdata[] = {  		.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,  		.mode_off[1] = IOMUXC_GPR12,  		.mode_mask[1] = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE, -		.epc_features = &imx8m_pcie_epc_features, +		.epc_features = &imx8q_pcie_epc_features,  		.init_phy = imx8mq_pcie_init_phy,  		.enable_ref_clk = imx8mm_pcie_enable_ref_clk,  	}, diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index c67601096c48..0fbf86c0b97e 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -814,14 +814,14 @@ static bool dw_pcie_ptm_context_update_visible(void *drvdata)  {  	struct dw_pcie *pci = drvdata; -	return (pci->mode == DW_PCIE_EP_TYPE) ? true : false; +	return pci->mode == DW_PCIE_EP_TYPE;  }  static bool dw_pcie_ptm_context_valid_visible(void *drvdata)  {  	struct dw_pcie *pci = drvdata; -	return (pci->mode == DW_PCIE_RC_TYPE) ? true : false; +	return pci->mode == DW_PCIE_RC_TYPE;  }  static bool dw_pcie_ptm_local_clock_visible(void *drvdata) @@ -834,38 +834,38 @@ static bool dw_pcie_ptm_master_clock_visible(void *drvdata)  {  	struct dw_pcie *pci = drvdata; -	return (pci->mode == DW_PCIE_EP_TYPE) ? true : false; +	return pci->mode == DW_PCIE_EP_TYPE;  }  static bool dw_pcie_ptm_t1_visible(void *drvdata)  {  	struct dw_pcie *pci = drvdata; -	return (pci->mode == DW_PCIE_EP_TYPE) ? true : false; +	return pci->mode == DW_PCIE_EP_TYPE;  }  static bool dw_pcie_ptm_t2_visible(void *drvdata)  {  	struct dw_pcie *pci = drvdata; -	return (pci->mode == DW_PCIE_RC_TYPE) ? true : false; +	return pci->mode == DW_PCIE_RC_TYPE;  }  static bool dw_pcie_ptm_t3_visible(void *drvdata)  {  	struct dw_pcie *pci = drvdata; -	return (pci->mode == DW_PCIE_RC_TYPE) ? true : false; +	return pci->mode == DW_PCIE_RC_TYPE;  }  static bool dw_pcie_ptm_t4_visible(void *drvdata)  {  	struct dw_pcie *pci = drvdata; -	return (pci->mode == DW_PCIE_EP_TYPE) ? true : false; +	return pci->mode == DW_PCIE_EP_TYPE;  } -const struct pcie_ptm_ops dw_pcie_ptm_ops = { +static const struct pcie_ptm_ops dw_pcie_ptm_ops = {  	.check_capability = dw_pcie_ptm_check_capability,  	.context_update_write = dw_pcie_ptm_context_update_write,  	.context_update_read = dw_pcie_ptm_context_update_read, diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 906277f9ffaf..952f8594b501 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -10,6 +10,7 @@  #include <linux/iopoll.h>  #include <linux/irqchip/chained_irq.h> +#include <linux/irqchip/irq-msi-lib.h>  #include <linux/irqdomain.h>  #include <linux/msi.h>  #include <linux/of_address.h> @@ -23,35 +24,21 @@  static struct pci_ops dw_pcie_ops;  static struct pci_ops dw_child_pcie_ops; -static void dw_msi_ack_irq(struct irq_data *d) -{ -	irq_chip_ack_parent(d); -} - -static void dw_msi_mask_irq(struct irq_data *d) -{ -	pci_msi_mask_irq(d); -	irq_chip_mask_parent(d); -} - -static void dw_msi_unmask_irq(struct irq_data *d) -{ -	pci_msi_unmask_irq(d); -	irq_chip_unmask_parent(d); -} - -static struct irq_chip dw_pcie_msi_irq_chip = { -	.name = "PCI-MSI", -	.irq_ack = dw_msi_ack_irq, -	.irq_mask = dw_msi_mask_irq, -	.irq_unmask = dw_msi_unmask_irq, -}; - -static struct msi_domain_info dw_pcie_msi_domain_info = { -	.flags	= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | -		  MSI_FLAG_NO_AFFINITY | MSI_FLAG_PCI_MSIX | -		  MSI_FLAG_MULTI_PCI_MSI, -	.chip	= &dw_pcie_msi_irq_chip, +#define DW_PCIE_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS		| \ +				    MSI_FLAG_USE_DEF_CHIP_OPS		| \ +				    MSI_FLAG_NO_AFFINITY		| \ +				    MSI_FLAG_PCI_MSI_MASK_PARENT) +#define DW_PCIE_MSI_FLAGS_SUPPORTED (MSI_FLAG_MULTI_PCI_MSI		| \ +				     MSI_FLAG_PCI_MSIX			| \ +				     MSI_GENERIC_FLAGS_MASK) + +static const struct msi_parent_ops dw_pcie_msi_parent_ops = { +	.required_flags		= DW_PCIE_MSI_FLAGS_REQUIRED, +	.supported_flags	= DW_PCIE_MSI_FLAGS_SUPPORTED, +	.bus_select_token	= DOMAIN_BUS_PCI_MSI, +	.chip_flags		= MSI_CHIP_FLAG_SET_ACK, +	.prefix			= "DW-", +	.init_dev_msi_info	= msi_lib_init_dev_msi_info,  };  /* MSI int handler */ @@ -227,30 +214,23 @@ static const struct irq_domain_ops dw_pcie_msi_domain_ops = {  int dw_pcie_allocate_domains(struct dw_pcie_rp *pp)  {  	struct dw_pcie *pci = to_dw_pcie_from_pp(pp); -	struct fwnode_handle *fwnode = of_fwnode_handle(pci->dev->of_node); - -	pp->irq_domain = irq_domain_create_linear(fwnode, pp->num_vectors, -					       &dw_pcie_msi_domain_ops, pp); +	struct irq_domain_info info = { +		.fwnode		= dev_fwnode(pci->dev), +		.ops		= &dw_pcie_msi_domain_ops, +		.size		= pp->num_vectors, +		.host_data	= pp, +	}; + +	pp->irq_domain = msi_create_parent_irq_domain(&info, &dw_pcie_msi_parent_ops);  	if (!pp->irq_domain) {  		dev_err(pci->dev, "Failed to create IRQ domain\n");  		return -ENOMEM;  	} -	irq_domain_update_bus_token(pp->irq_domain, DOMAIN_BUS_NEXUS); - -	pp->msi_domain = pci_msi_create_irq_domain(fwnode, -						   &dw_pcie_msi_domain_info, -						   pp->irq_domain); -	if (!pp->msi_domain) { -		dev_err(pci->dev, "Failed to create MSI domain\n"); -		irq_domain_remove(pp->irq_domain); -		return -ENOMEM; -	} -  	return 0;  } -static void dw_pcie_free_msi(struct dw_pcie_rp *pp) +void dw_pcie_free_msi(struct dw_pcie_rp *pp)  {  	u32 ctrl; @@ -260,22 +240,36 @@ static void dw_pcie_free_msi(struct dw_pcie_rp *pp)  							 NULL, NULL);  	} -	irq_domain_remove(pp->msi_domain);  	irq_domain_remove(pp->irq_domain);  } +EXPORT_SYMBOL_GPL(dw_pcie_free_msi); -static void dw_pcie_msi_init(struct dw_pcie_rp *pp) +void dw_pcie_msi_init(struct dw_pcie_rp *pp)  {  	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);  	u64 msi_target = (u64)pp->msi_data; +	u32 ctrl, num_ctrls;  	if (!pci_msi_enabled() || !pp->has_msi_ctrl)  		return; +	num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; + +	/* Initialize IRQ Status array */ +	for (ctrl = 0; ctrl < num_ctrls; ctrl++) { +		dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + +				    (ctrl * MSI_REG_CTRL_BLOCK_SIZE), +				    pp->irq_mask[ctrl]); +		dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_ENABLE + +				    (ctrl * MSI_REG_CTRL_BLOCK_SIZE), +				    ~0); +	} +  	/* Program the msi_data */  	dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_LO, lower_32_bits(msi_target));  	dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_HI, upper_32_bits(msi_target));  } +EXPORT_SYMBOL_GPL(dw_pcie_msi_init);  static int dw_pcie_parse_split_msi_irq(struct dw_pcie_rp *pp)  { @@ -317,7 +311,7 @@ static int dw_pcie_parse_split_msi_irq(struct dw_pcie_rp *pp)  	return 0;  } -static int dw_pcie_msi_host_init(struct dw_pcie_rp *pp) +int dw_pcie_msi_host_init(struct dw_pcie_rp *pp)  {  	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);  	struct device *dev = pci->dev; @@ -391,6 +385,7 @@ static int dw_pcie_msi_host_init(struct dw_pcie_rp *pp)  	return 0;  } +EXPORT_SYMBOL_GPL(dw_pcie_msi_host_init);  static void dw_pcie_host_request_msg_tlp_res(struct dw_pcie_rp *pp)  { @@ -909,7 +904,7 @@ static void dw_pcie_config_presets(struct dw_pcie_rp *pp)  int dw_pcie_setup_rc(struct dw_pcie_rp *pp)  {  	struct dw_pcie *pci = to_dw_pcie_from_pp(pp); -	u32 val, ctrl, num_ctrls; +	u32 val;  	int ret;  	/* @@ -920,20 +915,6 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp)  	dw_pcie_setup(pci); -	if (pp->has_msi_ctrl) { -		num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; - -		/* Initialize IRQ Status array */ -		for (ctrl = 0; ctrl < num_ctrls; ctrl++) { -			dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + -					    (ctrl * MSI_REG_CTRL_BLOCK_SIZE), -					    pp->irq_mask[ctrl]); -			dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_ENABLE + -					    (ctrl * MSI_REG_CTRL_BLOCK_SIZE), -					    ~0); -		} -	} -  	dw_pcie_msi_init(pp);  	/* Setup RC BARs */ diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 4d794964fa0f..89aad5a08928 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -702,18 +702,26 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci)  	int retries;  	/* Check if the link is up or not */ -	for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { +	for (retries = 0; retries < PCIE_LINK_WAIT_MAX_RETRIES; retries++) {  		if (dw_pcie_link_up(pci))  			break; -		msleep(LINK_WAIT_SLEEP_MS); +		msleep(PCIE_LINK_WAIT_SLEEP_MS);  	} -	if (retries >= LINK_WAIT_MAX_RETRIES) { +	if (retries >= PCIE_LINK_WAIT_MAX_RETRIES) {  		dev_info(pci->dev, "Phy link never came up\n");  		return -ETIMEDOUT;  	} +	/* +	 * As per PCIe r6.0, sec 6.6.1, a Downstream Port that supports Link +	 * speeds greater than 5.0 GT/s, software must wait a minimum of 100 ms +	 * after Link training completes before sending a Configuration Request. +	 */ +	if (pci->max_link_speed > 2) +		msleep(PCIE_RESET_CONFIG_WAIT_MS); +  	offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);  	val = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA); diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index ce9e18554e42..00f52d472dcd 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -62,10 +62,6 @@  #define dw_pcie_cap_set(_pci, _cap) \  	set_bit(DW_PCIE_CAP_ ## _cap, &(_pci)->caps) -/* Parameters for the waiting for link up routine */ -#define LINK_WAIT_MAX_RETRIES		10 -#define LINK_WAIT_SLEEP_MS		90 -  /* Parameters for the waiting for iATU enabled routine */  #define LINK_WAIT_MAX_IATU_RETRIES	5  #define LINK_WAIT_IATU			9 @@ -417,7 +413,6 @@ struct dw_pcie_rp {  	const struct dw_pcie_host_ops *ops;  	int			msi_irq[MAX_MSI_CTRLS];  	struct irq_domain	*irq_domain; -	struct irq_domain	*msi_domain;  	dma_addr_t		msi_data;  	struct irq_chip		*msi_irq_chip;  	u32			num_vectors; @@ -759,6 +754,9 @@ static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci)  int dw_pcie_suspend_noirq(struct dw_pcie *pci);  int dw_pcie_resume_noirq(struct dw_pcie *pci);  irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp); +void dw_pcie_msi_init(struct dw_pcie_rp *pp); +int dw_pcie_msi_host_init(struct dw_pcie_rp *pp); +void dw_pcie_free_msi(struct dw_pcie_rp *pp);  int dw_pcie_setup_rc(struct dw_pcie_rp *pp);  int dw_pcie_host_init(struct dw_pcie_rp *pp);  void dw_pcie_host_deinit(struct dw_pcie_rp *pp); @@ -781,6 +779,17 @@ static inline irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp)  	return IRQ_NONE;  } +static inline void dw_pcie_msi_init(struct dw_pcie_rp *pp) +{ } + +static inline int dw_pcie_msi_host_init(struct dw_pcie_rp *pp) +{ +	return -ENODEV; +} + +static inline void dw_pcie_free_msi(struct dw_pcie_rp *pp) +{ } +  static inline int dw_pcie_setup_rc(struct dw_pcie_rp *pp)  {  	return 0; diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 93171a392879..b5f5eee5a50e 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -58,6 +58,8 @@  /* Hot Reset Control Register */  #define PCIE_CLIENT_HOT_RESET_CTRL	0x180 +#define  PCIE_LTSSM_APP_DLY2_EN		BIT(1) +#define  PCIE_LTSSM_APP_DLY2_DONE	BIT(3)  #define  PCIE_LTSSM_ENABLE_ENHANCE	BIT(4)  /* LTSSM Status Register */ @@ -458,6 +460,7 @@ static irqreturn_t rockchip_pcie_rc_sys_irq_thread(int irq, void *arg)  	if (reg & PCIE_RDLH_LINK_UP_CHGED) {  		if (rockchip_pcie_link_up(pci)) { +			msleep(PCIE_RESET_CONFIG_WAIT_MS);  			dev_dbg(dev, "Received Link up event. Starting enumeration!\n");  			/* Rescan the bus to enumerate endpoint devices */  			pci_lock_rescan_remove(); @@ -474,7 +477,7 @@ static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg)  	struct rockchip_pcie *rockchip = arg;  	struct dw_pcie *pci = &rockchip->pci;  	struct device *dev = pci->dev; -	u32 reg; +	u32 reg, val;  	reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_MISC);  	rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC); @@ -485,6 +488,10 @@ static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg)  	if (reg & PCIE_LINK_REQ_RST_NOT_INT) {  		dev_dbg(dev, "hot reset or link-down reset\n");  		dw_pcie_ep_linkdown(&pci->ep); +		/* Stop delaying link training. */ +		val = HIWORD_UPDATE_BIT(PCIE_LTSSM_APP_DLY2_DONE); +		rockchip_pcie_writel_apb(rockchip, val, +					 PCIE_CLIENT_HOT_RESET_CTRL);  	}  	if (reg & PCIE_RDLH_LINK_UP_CHGED) { @@ -566,8 +573,11 @@ static int rockchip_pcie_configure_ep(struct platform_device *pdev,  		return ret;  	} -	/* LTSSM enable control mode */ -	val = HIWORD_UPDATE_BIT(PCIE_LTSSM_ENABLE_ENHANCE); +	/* +	 * LTSSM enable control mode, and automatically delay link training on +	 * hot reset/link-down reset. +	 */ +	val = HIWORD_UPDATE_BIT(PCIE_LTSSM_ENABLE_ENHANCE | PCIE_LTSSM_APP_DLY2_EN);  	rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL);  	rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_EP_MODE, diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index c789e3f85655..294babe1816e 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -21,7 +21,9 @@  #include <linux/limits.h>  #include <linux/init.h>  #include <linux/of.h> +#include <linux/of_pci.h>  #include <linux/pci.h> +#include <linux/pci-ecam.h>  #include <linux/pm_opp.h>  #include <linux/pm_runtime.h>  #include <linux/platform_device.h> @@ -34,6 +36,7 @@  #include <linux/units.h>  #include "../../pci.h" +#include "../pci-host-common.h"  #include "pcie-designware.h"  #include "pcie-qcom-common.h" @@ -255,13 +258,21 @@ struct qcom_pcie_ops {    * @ops: qcom PCIe ops structure    * @override_no_snoop: Override NO_SNOOP attribute in TLP to enable cache    * snooping +  * @firmware_managed: Set if the Root Complex is firmware managed    */  struct qcom_pcie_cfg {  	const struct qcom_pcie_ops *ops;  	bool override_no_snoop; +	bool firmware_managed;  	bool no_l0s;  }; +struct qcom_pcie_port { +	struct list_head list; +	struct gpio_desc *reset; +	struct phy *phy; +}; +  struct qcom_pcie {  	struct dw_pcie *pci;  	void __iomem *parf;			/* DT parf */ @@ -274,24 +285,37 @@ struct qcom_pcie {  	struct icc_path *icc_cpu;  	const struct qcom_pcie_cfg *cfg;  	struct dentry *debugfs; +	struct list_head ports;  	bool suspended;  	bool use_pm_opp;  };  #define to_qcom_pcie(x)		dev_get_drvdata((x)->dev) -static void qcom_ep_reset_assert(struct qcom_pcie *pcie) +static void qcom_perst_assert(struct qcom_pcie *pcie, bool assert)  { -	gpiod_set_value_cansleep(pcie->reset, 1); +	struct qcom_pcie_port *port; +	int val = assert ? 1 : 0; + +	if (list_empty(&pcie->ports)) +		gpiod_set_value_cansleep(pcie->reset, val); +	else +		list_for_each_entry(port, &pcie->ports, list) +			gpiod_set_value_cansleep(port->reset, val); +  	usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);  } +static void qcom_ep_reset_assert(struct qcom_pcie *pcie) +{ +	qcom_perst_assert(pcie, true); +} +  static void qcom_ep_reset_deassert(struct qcom_pcie *pcie)  {  	/* Ensure that PERST has been asserted for at least 100 ms */  	msleep(PCIE_T_PVPERL_MS); -	gpiod_set_value_cansleep(pcie->reset, 0); -	usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500); +	qcom_perst_assert(pcie, false);  }  static int qcom_pcie_start_link(struct dw_pcie *pci) @@ -1229,6 +1253,59 @@ static bool qcom_pcie_link_up(struct dw_pcie *pci)  	return val & PCI_EXP_LNKSTA_DLLLA;  } +static void qcom_pcie_phy_exit(struct qcom_pcie *pcie) +{ +	struct qcom_pcie_port *port; + +	if (list_empty(&pcie->ports)) +		phy_exit(pcie->phy); +	else +		list_for_each_entry(port, &pcie->ports, list) +			phy_exit(port->phy); +} + +static void qcom_pcie_phy_power_off(struct qcom_pcie *pcie) +{ +	struct qcom_pcie_port *port; + +	if (list_empty(&pcie->ports)) { +		phy_power_off(pcie->phy); +	} else { +		list_for_each_entry(port, &pcie->ports, list) +			phy_power_off(port->phy); +	} +} + +static int qcom_pcie_phy_power_on(struct qcom_pcie *pcie) +{ +	struct qcom_pcie_port *port; +	int ret = 0; + +	if (list_empty(&pcie->ports)) { +		ret = phy_set_mode_ext(pcie->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC); +		if (ret) +			return ret; + +		ret = phy_power_on(pcie->phy); +		if (ret) +			return ret; +	} else { +		list_for_each_entry(port, &pcie->ports, list) { +			ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC); +			if (ret) +				return ret; + +			ret = phy_power_on(port->phy); +			if (ret) { +				qcom_pcie_phy_power_off(pcie); +				return ret; +			} +		} +	} + +	return ret; +} +  static int qcom_pcie_host_init(struct dw_pcie_rp *pp)  {  	struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -1241,11 +1318,7 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)  	if (ret)  		return ret; -	ret = phy_set_mode_ext(pcie->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC); -	if (ret) -		goto err_deinit; - -	ret = phy_power_on(pcie->phy); +	ret = qcom_pcie_phy_power_on(pcie);  	if (ret)  		goto err_deinit; @@ -1268,7 +1341,7 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)  err_assert_reset:  	qcom_ep_reset_assert(pcie);  err_disable_phy: -	phy_power_off(pcie->phy); +	qcom_pcie_phy_power_off(pcie);  err_deinit:  	pcie->cfg->ops->deinit(pcie); @@ -1281,7 +1354,7 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp)  	struct qcom_pcie *pcie = to_qcom_pcie(pci);  	qcom_ep_reset_assert(pcie); -	phy_power_off(pcie->phy); +	qcom_pcie_phy_power_off(pcie);  	pcie->cfg->ops->deinit(pcie);  } @@ -1426,6 +1499,10 @@ static const struct qcom_pcie_cfg cfg_sc8280xp = {  	.no_l0s = true,  }; +static const struct qcom_pcie_cfg cfg_fw_managed = { +	.firmware_managed = true, +}; +  static const struct dw_pcie_ops dw_pcie_ops = {  	.link_up = qcom_pcie_link_up,  	.start_link = qcom_pcie_start_link, @@ -1564,6 +1641,7 @@ static irqreturn_t qcom_pcie_global_irq_thread(int irq, void *data)  	writel_relaxed(status, pcie->parf + PARF_INT_ALL_CLEAR);  	if (FIELD_GET(PARF_INT_ALL_LINK_UP, status)) { +		msleep(PCIE_RESET_CONFIG_WAIT_MS);  		dev_dbg(dev, "Received Link up event. Starting enumeration!\n");  		/* Rescan the bus to enumerate endpoint devices */  		pci_lock_rescan_remove(); @@ -1579,10 +1657,128 @@ static irqreturn_t qcom_pcie_global_irq_thread(int irq, void *data)  	return IRQ_HANDLED;  } +static void qcom_pci_free_msi(void *ptr) +{ +	struct dw_pcie_rp *pp = (struct dw_pcie_rp *)ptr; + +	if (pp && pp->has_msi_ctrl) +		dw_pcie_free_msi(pp); +} + +static int qcom_pcie_ecam_host_init(struct pci_config_window *cfg) +{ +	struct device *dev = cfg->parent; +	struct dw_pcie_rp *pp; +	struct dw_pcie *pci; +	int ret; + +	pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); +	if (!pci) +		return -ENOMEM; + +	pci->dev = dev; +	pp = &pci->pp; +	pci->dbi_base = cfg->win; +	pp->num_vectors = MSI_DEF_NUM_VECTORS; + +	ret = dw_pcie_msi_host_init(pp); +	if (ret) +		return ret; + +	pp->has_msi_ctrl = true; +	dw_pcie_msi_init(pp); + +	return devm_add_action_or_reset(dev, qcom_pci_free_msi, pp); +} + +static const struct pci_ecam_ops pci_qcom_ecam_ops = { +	.init		= qcom_pcie_ecam_host_init, +	.pci_ops	= { +		.map_bus	= pci_ecam_map_bus, +		.read		= pci_generic_config_read, +		.write		= pci_generic_config_write, +	} +}; + +static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node) +{ +	struct device *dev = pcie->pci->dev; +	struct qcom_pcie_port *port; +	struct gpio_desc *reset; +	struct phy *phy; +	int ret; + +	reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(node), +				      "reset", GPIOD_OUT_HIGH, "PERST#"); +	if (IS_ERR(reset)) +		return PTR_ERR(reset); + +	phy = devm_of_phy_get(dev, node, NULL); +	if (IS_ERR(phy)) +		return PTR_ERR(phy); + +	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); +	if (!port) +		return -ENOMEM; + +	ret = phy_init(phy); +	if (ret) +		return ret; + +	port->reset = reset; +	port->phy = phy; +	INIT_LIST_HEAD(&port->list); +	list_add_tail(&port->list, &pcie->ports); + +	return 0; +} + +static int qcom_pcie_parse_ports(struct qcom_pcie *pcie) +{ +	struct device *dev = pcie->pci->dev; +	struct qcom_pcie_port *port, *tmp; +	int ret = -ENOENT; + +	for_each_available_child_of_node_scoped(dev->of_node, of_port) { +		ret = qcom_pcie_parse_port(pcie, of_port); +		if (ret) +			goto err_port_del; +	} + +	return ret; + +err_port_del: +	list_for_each_entry_safe(port, tmp, &pcie->ports, list) +		list_del(&port->list); + +	return ret; +} + +static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie) +{ +	struct device *dev = pcie->pci->dev; +	int ret; + +	pcie->phy = devm_phy_optional_get(dev, "pciephy"); +	if (IS_ERR(pcie->phy)) +		return PTR_ERR(pcie->phy); + +	pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH); +	if (IS_ERR(pcie->reset)) +		return PTR_ERR(pcie->reset); + +	ret = phy_init(pcie->phy); +	if (ret) +		return ret; + +	return 0; +} +  static int qcom_pcie_probe(struct platform_device *pdev)  {  	const struct qcom_pcie_cfg *pcie_cfg;  	unsigned long max_freq = ULONG_MAX; +	struct qcom_pcie_port *port, *tmp;  	struct device *dev = &pdev->dev;  	struct dev_pm_opp *opp;  	struct qcom_pcie *pcie; @@ -1593,24 +1789,64 @@ static int qcom_pcie_probe(struct platform_device *pdev)  	char *name;  	pcie_cfg = of_device_get_match_data(dev); -	if (!pcie_cfg || !pcie_cfg->ops) { -		dev_err(dev, "Invalid platform data\n"); -		return -EINVAL; +	if (!pcie_cfg) { +		dev_err(dev, "No platform data\n"); +		return -ENODATA;  	} -	pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); -	if (!pcie) -		return -ENOMEM; - -	pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); -	if (!pci) -		return -ENOMEM; +	if (!pcie_cfg->firmware_managed && !pcie_cfg->ops) { +		dev_err(dev, "No platform ops\n"); +		return -ENODATA; +	}  	pm_runtime_enable(dev);  	ret = pm_runtime_get_sync(dev);  	if (ret < 0)  		goto err_pm_runtime_put; +	if (pcie_cfg->firmware_managed) { +		struct pci_host_bridge *bridge; +		struct pci_config_window *cfg; + +		bridge = devm_pci_alloc_host_bridge(dev, 0); +		if (!bridge) { +			ret = -ENOMEM; +			goto err_pm_runtime_put; +		} + +		/* Parse and map our ECAM configuration space area */ +		cfg = pci_host_common_ecam_create(dev, bridge, +				&pci_qcom_ecam_ops); +		if (IS_ERR(cfg)) { +			ret = PTR_ERR(cfg); +			goto err_pm_runtime_put; +		} + +		bridge->sysdata = cfg; +		bridge->ops = (struct pci_ops *)&pci_qcom_ecam_ops.pci_ops; +		bridge->msi_domain = true; + +		ret = pci_host_probe(bridge); +		if (ret) +			goto err_pm_runtime_put; + +		return 0; +	} + +	pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); +	if (!pcie) { +		ret = -ENOMEM; +		goto err_pm_runtime_put; +	} + +	pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); +	if (!pci) { +		ret = -ENOMEM; +		goto err_pm_runtime_put; +	} + +	INIT_LIST_HEAD(&pcie->ports); +  	pci->dev = dev;  	pci->ops = &dw_pcie_ops;  	pp = &pci->pp; @@ -1619,12 +1855,6 @@ static int qcom_pcie_probe(struct platform_device *pdev)  	pcie->cfg = pcie_cfg; -	pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH); -	if (IS_ERR(pcie->reset)) { -		ret = PTR_ERR(pcie->reset); -		goto err_pm_runtime_put; -	} -  	pcie->parf = devm_platform_ioremap_resource_byname(pdev, "parf");  	if (IS_ERR(pcie->parf)) {  		ret = PTR_ERR(pcie->parf); @@ -1647,12 +1877,6 @@ static int qcom_pcie_probe(struct platform_device *pdev)  		}  	} -	pcie->phy = devm_phy_optional_get(dev, "pciephy"); -	if (IS_ERR(pcie->phy)) { -		ret = PTR_ERR(pcie->phy); -		goto err_pm_runtime_put; -	} -  	/* OPP table is optional */  	ret = devm_pm_opp_of_add_table(dev);  	if (ret && ret != -ENODEV) { @@ -1699,9 +1923,23 @@ static int qcom_pcie_probe(struct platform_device *pdev)  	pp->ops = &qcom_pcie_dw_ops; -	ret = phy_init(pcie->phy); -	if (ret) -		goto err_pm_runtime_put; +	ret = qcom_pcie_parse_ports(pcie); +	if (ret) { +		if (ret != -ENOENT) { +			dev_err_probe(pci->dev, ret, +				      "Failed to parse Root Port: %d\n", ret); +			goto err_pm_runtime_put; +		} + +		/* +		 * In the case of properties not populated in Root Port node, +		 * fallback to the legacy method of parsing the Host Bridge +		 * node. This is to maintain DT backwards compatibility. +		 */ +		ret = qcom_pcie_parse_legacy_binding(pcie); +		if (ret) +			goto err_pm_runtime_put; +	}  	platform_set_drvdata(pdev, pcie); @@ -1746,7 +1984,9 @@ static int qcom_pcie_probe(struct platform_device *pdev)  err_host_deinit:  	dw_pcie_host_deinit(pp);  err_phy_exit: -	phy_exit(pcie->phy); +	qcom_pcie_phy_exit(pcie); +	list_for_each_entry_safe(port, tmp, &pcie->ports, list) +		list_del(&port->list);  err_pm_runtime_put:  	pm_runtime_put(dev);  	pm_runtime_disable(dev); @@ -1756,9 +1996,13 @@ err_pm_runtime_put:  static int qcom_pcie_suspend_noirq(struct device *dev)  { -	struct qcom_pcie *pcie = dev_get_drvdata(dev); +	struct qcom_pcie *pcie;  	int ret = 0; +	pcie = dev_get_drvdata(dev); +	if (!pcie) +		return 0; +  	/*  	 * Set minimum bandwidth required to keep data path functional during  	 * suspend. @@ -1812,9 +2056,13 @@ static int qcom_pcie_suspend_noirq(struct device *dev)  static int qcom_pcie_resume_noirq(struct device *dev)  { -	struct qcom_pcie *pcie = dev_get_drvdata(dev); +	struct qcom_pcie *pcie;  	int ret; +	pcie = dev_get_drvdata(dev); +	if (!pcie) +		return 0; +  	if (pm_suspend_target_state != PM_SUSPEND_MEM) {  		ret = icc_enable(pcie->icc_cpu);  		if (ret) { @@ -1849,6 +2097,7 @@ static const struct of_device_id qcom_pcie_match[] = {  	{ .compatible = "qcom,pcie-ipq9574", .data = &cfg_2_9_0 },  	{ .compatible = "qcom,pcie-msm8996", .data = &cfg_2_3_2 },  	{ .compatible = "qcom,pcie-qcs404", .data = &cfg_2_4_0 }, +	{ .compatible = "qcom,pcie-sa8255p", .data = &cfg_fw_managed },  	{ .compatible = "qcom,pcie-sa8540p", .data = &cfg_sc8280xp },  	{ .compatible = "qcom,pcie-sa8775p", .data = &cfg_1_34_0},  	{ .compatible = "qcom,pcie-sc7280", .data = &cfg_1_9_0 }, diff --git a/drivers/pci/controller/dwc/pcie-sophgo.c b/drivers/pci/controller/dwc/pcie-sophgo.c new file mode 100644 index 000000000000..ad4baaa34ffa --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-sophgo.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sophgo DesignWare based PCIe host controller driver + */ + +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/platform_device.h> + +#include "pcie-designware.h" + +#define to_sophgo_pcie(x)		dev_get_drvdata((x)->dev) + +#define PCIE_INT_SIGNAL			0xc48 +#define PCIE_INT_EN			0xca0 + +#define PCIE_INT_SIGNAL_INTX		GENMASK(8, 5) + +#define PCIE_INT_EN_INTX		GENMASK(4, 1) +#define PCIE_INT_EN_INT_MSI		BIT(5) + +struct sophgo_pcie { +	struct dw_pcie		pci; +	void __iomem		*app_base; +	struct clk_bulk_data	*clks; +	unsigned int		clk_cnt; +	struct irq_domain	*irq_domain; +}; + +static int sophgo_pcie_readl_app(struct sophgo_pcie *sophgo, u32 reg) +{ +	return readl_relaxed(sophgo->app_base + reg); +} + +static void sophgo_pcie_writel_app(struct sophgo_pcie *sophgo, u32 val, u32 reg) +{ +	writel_relaxed(val, sophgo->app_base + reg); +} + +static void sophgo_pcie_intx_handler(struct irq_desc *desc) +{ +	struct dw_pcie_rp *pp = irq_desc_get_handler_data(desc); +	struct irq_chip *chip = irq_desc_get_chip(desc); +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp); +	struct sophgo_pcie *sophgo = to_sophgo_pcie(pci); +	unsigned long hwirq, reg; + +	chained_irq_enter(chip, desc); + +	reg = sophgo_pcie_readl_app(sophgo, PCIE_INT_SIGNAL); +	reg = FIELD_GET(PCIE_INT_SIGNAL_INTX, reg); + +	for_each_set_bit(hwirq, ®, PCI_NUM_INTX) +		generic_handle_domain_irq(sophgo->irq_domain, hwirq); + +	chained_irq_exit(chip, desc); +} + +static void sophgo_intx_irq_mask(struct irq_data *d) +{ +	struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp); +	struct sophgo_pcie *sophgo = to_sophgo_pcie(pci); +	unsigned long flags; +	u32 val; + +	raw_spin_lock_irqsave(&pp->lock, flags); + +	val = sophgo_pcie_readl_app(sophgo, PCIE_INT_EN); +	val &= ~FIELD_PREP(PCIE_INT_EN_INTX, BIT(d->hwirq)); +	sophgo_pcie_writel_app(sophgo, val, PCIE_INT_EN); + +	raw_spin_unlock_irqrestore(&pp->lock, flags); +}; + +static void sophgo_intx_irq_unmask(struct irq_data *d) +{ +	struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d); +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp); +	struct sophgo_pcie *sophgo = to_sophgo_pcie(pci); +	unsigned long flags; +	u32 val; + +	raw_spin_lock_irqsave(&pp->lock, flags); + +	val = sophgo_pcie_readl_app(sophgo, PCIE_INT_EN); +	val |= FIELD_PREP(PCIE_INT_EN_INTX, BIT(d->hwirq)); +	sophgo_pcie_writel_app(sophgo, val, PCIE_INT_EN); + +	raw_spin_unlock_irqrestore(&pp->lock, flags); +}; + +static struct irq_chip sophgo_intx_irq_chip = { +	.name			= "INTx", +	.irq_mask		= sophgo_intx_irq_mask, +	.irq_unmask		= sophgo_intx_irq_unmask, +}; + +static int sophgo_pcie_intx_map(struct irq_domain *domain, unsigned int irq, +				irq_hw_number_t hwirq) +{ +	irq_set_chip_and_handler(irq, &sophgo_intx_irq_chip, handle_level_irq); +	irq_set_chip_data(irq, domain->host_data); + +	return 0; +} + +static const struct irq_domain_ops intx_domain_ops = { +	.map = sophgo_pcie_intx_map, +}; + +static int sophgo_pcie_init_irq_domain(struct dw_pcie_rp *pp) +{ +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp); +	struct sophgo_pcie *sophgo = to_sophgo_pcie(pci); +	struct device *dev = sophgo->pci.dev; +	struct fwnode_handle *intc; +	int irq; + +	intc = device_get_named_child_node(dev, "interrupt-controller"); +	if (!intc) { +		dev_err(dev, "missing child interrupt-controller node\n"); +		return -ENODEV; +	} + +	irq = fwnode_irq_get(intc, 0); +	if (irq < 0) { +		dev_err(dev, "failed to get INTx irq number\n"); +		fwnode_handle_put(intc); +		return irq; +	} + +	sophgo->irq_domain = irq_domain_create_linear(intc, PCI_NUM_INTX, +						      &intx_domain_ops, pp); +	fwnode_handle_put(intc); +	if (!sophgo->irq_domain) { +		dev_err(dev, "failed to get a INTx irq domain\n"); +		return -EINVAL; +	} + +	return irq; +} + +static void sophgo_pcie_msi_enable(struct dw_pcie_rp *pp) +{ +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp); +	struct sophgo_pcie *sophgo = to_sophgo_pcie(pci); +	unsigned long flags; +	u32 val; + +	raw_spin_lock_irqsave(&pp->lock, flags); + +	val = sophgo_pcie_readl_app(sophgo, PCIE_INT_EN); +	val |= PCIE_INT_EN_INT_MSI; +	sophgo_pcie_writel_app(sophgo, val, PCIE_INT_EN); + +	raw_spin_unlock_irqrestore(&pp->lock, flags); +} + +static int sophgo_pcie_host_init(struct dw_pcie_rp *pp) +{ +	int irq; + +	irq = sophgo_pcie_init_irq_domain(pp); +	if (irq < 0) +		return irq; + +	irq_set_chained_handler_and_data(irq, sophgo_pcie_intx_handler, pp); + +	sophgo_pcie_msi_enable(pp); + +	return 0; +} + +static const struct dw_pcie_host_ops sophgo_pcie_host_ops = { +	.init = sophgo_pcie_host_init, +}; + +static int sophgo_pcie_clk_init(struct sophgo_pcie *sophgo) +{ +	struct device *dev = sophgo->pci.dev; +	int ret; + +	ret = devm_clk_bulk_get_all_enabled(dev, &sophgo->clks); +	if (ret < 0) +		return dev_err_probe(dev, ret, "failed to get clocks\n"); + +	sophgo->clk_cnt = ret; + +	return 0; +} + +static int sophgo_pcie_resource_get(struct platform_device *pdev, +				    struct sophgo_pcie *sophgo) +{ +	sophgo->app_base = devm_platform_ioremap_resource_byname(pdev, "app"); +	if (IS_ERR(sophgo->app_base)) +		return dev_err_probe(&pdev->dev, PTR_ERR(sophgo->app_base), +				     "failed to map app registers\n"); + +	return 0; +} + +static int sophgo_pcie_configure_rc(struct sophgo_pcie *sophgo) +{ +	struct dw_pcie_rp *pp; + +	pp = &sophgo->pci.pp; +	pp->ops = &sophgo_pcie_host_ops; + +	return dw_pcie_host_init(pp); +} + +static int sophgo_pcie_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct sophgo_pcie *sophgo; +	int ret; + +	sophgo = devm_kzalloc(dev, sizeof(*sophgo), GFP_KERNEL); +	if (!sophgo) +		return -ENOMEM; + +	platform_set_drvdata(pdev, sophgo); + +	sophgo->pci.dev = dev; + +	ret = sophgo_pcie_resource_get(pdev, sophgo); +	if (ret) +		return ret; + +	ret = sophgo_pcie_clk_init(sophgo); +	if (ret) +		return ret; + +	return sophgo_pcie_configure_rc(sophgo); +} + +static const struct of_device_id sophgo_pcie_of_match[] = { +	{ .compatible = "sophgo,sg2044-pcie" }, +	{ } +}; +MODULE_DEVICE_TABLE(of, sophgo_pcie_of_match); + +static struct platform_driver sophgo_pcie_driver = { +	.driver = { +		.name = "sophgo-pcie", +		.of_match_table = sophgo_pcie_of_match, +		.suppress_bind_attrs = true, +	}, +	.probe = sophgo_pcie_probe, +}; +builtin_platform_driver(sophgo_pcie_driver);  | 
