diff options
Diffstat (limited to 'drivers/pci/controller/dwc')
| -rw-r--r-- | drivers/pci/controller/dwc/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/Makefile | 1 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pci-imx6.c | 13 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pci-layerscape.c | 1 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-armada8k.c | 1 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-bt1.c | 643 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware-ep.c | 48 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware-host.c | 47 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware.c | 266 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware.h | 63 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-histb.c | 39 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-qcom-ep.c | 5 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-qcom.c | 83 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-tegra194.c | 1 | 
14 files changed, 1095 insertions, 125 deletions
| diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index f3c462130627..a0d2713f0e88 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -222,6 +222,15 @@ config PCIE_ARTPEC6_EP  	  Enables support for the PCIe controller in the ARTPEC-6 SoC to work in  	  endpoint mode. This uses the DesignWare core. +config PCIE_BT1 +	tristate "Baikal-T1 PCIe controller" +	depends on MIPS_BAIKAL_T1 || COMPILE_TEST +	depends on PCI_MSI_IRQ_DOMAIN +	select PCIE_DW_HOST +	help +	  Enables support for the PCIe controller in the Baikal-T1 SoC to work +	  in host mode. It's based on the Synopsys DWC PCIe v4.60a IP-core. +  config PCIE_ROCKCHIP_DW_HOST  	bool "Rockchip DesignWare PCIe controller"  	select PCIE_DW diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index 8ba7b67f5e50..bf5c311875a1 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o  obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o  obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o  obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o +obj-$(CONFIG_PCIE_BT1) += pcie-bt1.o  obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o  obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o  obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 2616585ca5f8..1dde5c579edc 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -952,12 +952,6 @@ static int imx6_pcie_host_init(struct dw_pcie_rp *pp)  		}  	} -	ret = imx6_pcie_deassert_core_reset(imx6_pcie); -	if (ret < 0) { -		dev_err(dev, "pcie deassert core reset failed: %d\n", ret); -		goto err_phy_off; -	} -  	if (imx6_pcie->phy) {  		ret = phy_power_on(imx6_pcie->phy);  		if (ret) { @@ -965,6 +959,13 @@ static int imx6_pcie_host_init(struct dw_pcie_rp *pp)  			goto err_phy_off;  		}  	} + +	ret = imx6_pcie_deassert_core_reset(imx6_pcie); +	if (ret < 0) { +		dev_err(dev, "pcie deassert core reset failed: %d\n", ret); +		goto err_phy_off; +	} +  	imx6_setup_phy_mpll(imx6_pcie);  	return 0; diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c index 879b8692f96a..ed5fb492fe08 100644 --- a/drivers/pci/controller/dwc/pci-layerscape.c +++ b/drivers/pci/controller/dwc/pci-layerscape.c @@ -13,7 +13,6 @@  #include <linux/init.h>  #include <linux/of_pci.h>  #include <linux/of_platform.h> -#include <linux/of_irq.h>  #include <linux/of_address.h>  #include <linux/pci.h>  #include <linux/platform_device.h> diff --git a/drivers/pci/controller/dwc/pcie-armada8k.c b/drivers/pci/controller/dwc/pcie-armada8k.c index dc469ef8e99b..5c999e15c357 100644 --- a/drivers/pci/controller/dwc/pcie-armada8k.c +++ b/drivers/pci/controller/dwc/pcie-armada8k.c @@ -21,7 +21,6 @@  #include <linux/platform_device.h>  #include <linux/resource.h>  #include <linux/of_pci.h> -#include <linux/of_irq.h>  #include "pcie-designware.h" diff --git a/drivers/pci/controller/dwc/pcie-bt1.c b/drivers/pci/controller/dwc/pcie-bt1.c new file mode 100644 index 000000000000..3346770e6654 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-bt1.c @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC + * + * Authors: + *   Vadim Vlasov <[email protected]> + *   Serge Semin <[email protected]> + * + * Baikal-T1 PCIe controller driver + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/types.h> + +#include "pcie-designware.h" + +/* Baikal-T1 System CCU control registers */ +#define BT1_CCU_PCIE_CLKC			0x140 +#define BT1_CCU_PCIE_REQ_PCS_CLK		BIT(16) +#define BT1_CCU_PCIE_REQ_MAC_CLK		BIT(17) +#define BT1_CCU_PCIE_REQ_PIPE_CLK		BIT(18) + +#define BT1_CCU_PCIE_RSTC			0x144 +#define BT1_CCU_PCIE_REQ_LINK_RST		BIT(13) +#define BT1_CCU_PCIE_REQ_SMLH_RST		BIT(14) +#define BT1_CCU_PCIE_REQ_PHY_RST		BIT(16) +#define BT1_CCU_PCIE_REQ_CORE_RST		BIT(24) +#define BT1_CCU_PCIE_REQ_STICKY_RST		BIT(26) +#define BT1_CCU_PCIE_REQ_NSTICKY_RST		BIT(27) + +#define BT1_CCU_PCIE_PMSC			0x148 +#define BT1_CCU_PCIE_LTSSM_STATE_MASK		GENMASK(5, 0) +#define BT1_CCU_PCIE_LTSSM_DET_QUIET		0x00 +#define BT1_CCU_PCIE_LTSSM_DET_ACT		0x01 +#define BT1_CCU_PCIE_LTSSM_POLL_ACT		0x02 +#define BT1_CCU_PCIE_LTSSM_POLL_COMP		0x03 +#define BT1_CCU_PCIE_LTSSM_POLL_CONF		0x04 +#define BT1_CCU_PCIE_LTSSM_PRE_DET_QUIET	0x05 +#define BT1_CCU_PCIE_LTSSM_DET_WAIT		0x06 +#define BT1_CCU_PCIE_LTSSM_CFG_LNKWD_START	0x07 +#define BT1_CCU_PCIE_LTSSM_CFG_LNKWD_ACEPT	0x08 +#define BT1_CCU_PCIE_LTSSM_CFG_LNNUM_WAIT	0x09 +#define BT1_CCU_PCIE_LTSSM_CFG_LNNUM_ACEPT	0x0a +#define BT1_CCU_PCIE_LTSSM_CFG_COMPLETE		0x0b +#define BT1_CCU_PCIE_LTSSM_CFG_IDLE		0x0c +#define BT1_CCU_PCIE_LTSSM_RCVR_LOCK		0x0d +#define BT1_CCU_PCIE_LTSSM_RCVR_SPEED		0x0e +#define BT1_CCU_PCIE_LTSSM_RCVR_RCVRCFG		0x0f +#define BT1_CCU_PCIE_LTSSM_RCVR_IDLE		0x10 +#define BT1_CCU_PCIE_LTSSM_L0			0x11 +#define BT1_CCU_PCIE_LTSSM_L0S			0x12 +#define BT1_CCU_PCIE_LTSSM_L123_SEND_IDLE	0x13 +#define BT1_CCU_PCIE_LTSSM_L1_IDLE		0x14 +#define BT1_CCU_PCIE_LTSSM_L2_IDLE		0x15 +#define BT1_CCU_PCIE_LTSSM_L2_WAKE		0x16 +#define BT1_CCU_PCIE_LTSSM_DIS_ENTRY		0x17 +#define BT1_CCU_PCIE_LTSSM_DIS_IDLE		0x18 +#define BT1_CCU_PCIE_LTSSM_DISABLE		0x19 +#define BT1_CCU_PCIE_LTSSM_LPBK_ENTRY		0x1a +#define BT1_CCU_PCIE_LTSSM_LPBK_ACTIVE		0x1b +#define BT1_CCU_PCIE_LTSSM_LPBK_EXIT		0x1c +#define BT1_CCU_PCIE_LTSSM_LPBK_EXIT_TOUT	0x1d +#define BT1_CCU_PCIE_LTSSM_HOT_RST_ENTRY	0x1e +#define BT1_CCU_PCIE_LTSSM_HOT_RST		0x1f +#define BT1_CCU_PCIE_LTSSM_RCVR_EQ0		0x20 +#define BT1_CCU_PCIE_LTSSM_RCVR_EQ1		0x21 +#define BT1_CCU_PCIE_LTSSM_RCVR_EQ2		0x22 +#define BT1_CCU_PCIE_LTSSM_RCVR_EQ3		0x23 +#define BT1_CCU_PCIE_SMLH_LINKUP		BIT(6) +#define BT1_CCU_PCIE_RDLH_LINKUP		BIT(7) +#define BT1_CCU_PCIE_PM_LINKSTATE_L0S		BIT(8) +#define BT1_CCU_PCIE_PM_LINKSTATE_L1		BIT(9) +#define BT1_CCU_PCIE_PM_LINKSTATE_L2		BIT(10) +#define BT1_CCU_PCIE_L1_PENDING			BIT(12) +#define BT1_CCU_PCIE_REQ_EXIT_L1		BIT(14) +#define BT1_CCU_PCIE_LTSSM_RCVR_EQ		BIT(15) +#define BT1_CCU_PCIE_PM_DSTAT_MASK		GENMASK(18, 16) +#define BT1_CCU_PCIE_PM_PME_EN			BIT(20) +#define BT1_CCU_PCIE_PM_PME_STATUS		BIT(21) +#define BT1_CCU_PCIE_AUX_PM_EN			BIT(22) +#define BT1_CCU_PCIE_AUX_PWR_DET		BIT(23) +#define BT1_CCU_PCIE_WAKE_DET			BIT(24) +#define BT1_CCU_PCIE_TURNOFF_REQ		BIT(30) +#define BT1_CCU_PCIE_TURNOFF_ACK		BIT(31) + +#define BT1_CCU_PCIE_GENC			0x14c +#define BT1_CCU_PCIE_LTSSM_EN			BIT(1) +#define BT1_CCU_PCIE_DBI2_MODE			BIT(2) +#define BT1_CCU_PCIE_MGMT_EN			BIT(3) +#define BT1_CCU_PCIE_RXLANE_FLIP_EN		BIT(16) +#define BT1_CCU_PCIE_TXLANE_FLIP_EN		BIT(17) +#define BT1_CCU_PCIE_SLV_XFER_PEND		BIT(24) +#define BT1_CCU_PCIE_RCV_XFER_PEND		BIT(25) +#define BT1_CCU_PCIE_DBI_XFER_PEND		BIT(26) +#define BT1_CCU_PCIE_DMA_XFER_PEND		BIT(27) + +#define BT1_CCU_PCIE_LTSSM_LINKUP(_pmsc) \ +({ \ +	int __state = FIELD_GET(BT1_CCU_PCIE_LTSSM_STATE_MASK, _pmsc); \ +	__state >= BT1_CCU_PCIE_LTSSM_L0 && __state <= BT1_CCU_PCIE_LTSSM_L2_WAKE; \ +}) + +/* Baikal-T1 PCIe specific control registers */ +#define BT1_PCIE_AXI2MGM_LANENUM		0xd04 +#define BT1_PCIE_AXI2MGM_LANESEL_MASK		GENMASK(3, 0) + +#define BT1_PCIE_AXI2MGM_ADDRCTL		0xd08 +#define BT1_PCIE_AXI2MGM_PHYREG_ADDR_MASK	GENMASK(20, 0) +#define BT1_PCIE_AXI2MGM_READ_FLAG		BIT(29) +#define BT1_PCIE_AXI2MGM_DONE			BIT(30) +#define BT1_PCIE_AXI2MGM_BUSY			BIT(31) + +#define BT1_PCIE_AXI2MGM_WRITEDATA		0xd0c +#define BT1_PCIE_AXI2MGM_WDATA			GENMASK(15, 0) + +#define BT1_PCIE_AXI2MGM_READDATA		0xd10 +#define BT1_PCIE_AXI2MGM_RDATA			GENMASK(15, 0) + +/* Generic Baikal-T1 PCIe interface resources */ +#define BT1_PCIE_NUM_APP_CLKS			ARRAY_SIZE(bt1_pcie_app_clks) +#define BT1_PCIE_NUM_CORE_CLKS			ARRAY_SIZE(bt1_pcie_core_clks) +#define BT1_PCIE_NUM_APP_RSTS			ARRAY_SIZE(bt1_pcie_app_rsts) +#define BT1_PCIE_NUM_CORE_RSTS			ARRAY_SIZE(bt1_pcie_core_rsts) + +/* PCIe bus setup delays and timeouts */ +#define BT1_PCIE_RST_DELAY_MS			100 +#define BT1_PCIE_RUN_DELAY_US			100 +#define BT1_PCIE_REQ_DELAY_US			1 +#define BT1_PCIE_REQ_TIMEOUT_US			1000 +#define BT1_PCIE_LNK_DELAY_US			1000 +#define BT1_PCIE_LNK_TIMEOUT_US			1000000 + +static const enum dw_pcie_app_clk bt1_pcie_app_clks[] = { +	DW_PCIE_DBI_CLK, DW_PCIE_MSTR_CLK, DW_PCIE_SLV_CLK, +}; + +static const enum dw_pcie_core_clk bt1_pcie_core_clks[] = { +	DW_PCIE_REF_CLK, +}; + +static const enum dw_pcie_app_rst bt1_pcie_app_rsts[] = { +	DW_PCIE_MSTR_RST, DW_PCIE_SLV_RST, +}; + +static const enum dw_pcie_core_rst bt1_pcie_core_rsts[] = { +	DW_PCIE_NON_STICKY_RST, DW_PCIE_STICKY_RST, DW_PCIE_CORE_RST, +	DW_PCIE_PIPE_RST, DW_PCIE_PHY_RST, DW_PCIE_HOT_RST, DW_PCIE_PWR_RST, +}; + +struct bt1_pcie { +	struct dw_pcie dw; +	struct platform_device *pdev; +	struct regmap *sys_regs; +}; +#define to_bt1_pcie(_dw) container_of(_dw, struct bt1_pcie, dw) + +/* + * Baikal-T1 MMIO space must be read/written by the dword-aligned + * instructions. Note the methods are optimized to have the dword operations + * performed with minimum overhead as the most frequently used ones. + */ +static int bt1_pcie_read_mmio(void __iomem *addr, int size, u32 *val) +{ +	unsigned int ofs = (uintptr_t)addr & 0x3; + +	if (!IS_ALIGNED((uintptr_t)addr, size)) +		return -EINVAL; + +	*val = readl(addr - ofs) >> ofs * BITS_PER_BYTE; +	if (size == 4) { +		return 0; +	} else if (size == 2) { +		*val &= 0xffff; +		return 0; +	} else if (size == 1) { +		*val &= 0xff; +		return 0; +	} + +	return -EINVAL; +} + +static int bt1_pcie_write_mmio(void __iomem *addr, int size, u32 val) +{ +	unsigned int ofs = (uintptr_t)addr & 0x3; +	u32 tmp, mask; + +	if (!IS_ALIGNED((uintptr_t)addr, size)) +		return -EINVAL; + +	if (size == 4) { +		writel(val, addr); +		return 0; +	} else if (size == 2 || size == 1) { +		mask = GENMASK(size * BITS_PER_BYTE - 1, 0); +		tmp = readl(addr - ofs) & ~(mask << ofs * BITS_PER_BYTE); +		tmp |= (val & mask) << ofs * BITS_PER_BYTE; +		writel(tmp, addr - ofs); +		return 0; +	} + +	return -EINVAL; +} + +static u32 bt1_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, +			     size_t size) +{ +	int ret; +	u32 val; + +	ret = bt1_pcie_read_mmio(base + reg, size, &val); +	if (ret) { +		dev_err(pci->dev, "Read DBI address failed\n"); +		return ~0U; +	} + +	return val; +} + +static void bt1_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, +			       size_t size, u32 val) +{ +	int ret; + +	ret = bt1_pcie_write_mmio(base + reg, size, val); +	if (ret) +		dev_err(pci->dev, "Write DBI address failed\n"); +} + +static void bt1_pcie_write_dbi2(struct dw_pcie *pci, void __iomem *base, u32 reg, +				size_t size, u32 val) +{ +	struct bt1_pcie *btpci = to_bt1_pcie(pci); +	int ret; + +	regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC, +			   BT1_CCU_PCIE_DBI2_MODE, BT1_CCU_PCIE_DBI2_MODE); + +	ret = bt1_pcie_write_mmio(base + reg, size, val); +	if (ret) +		dev_err(pci->dev, "Write DBI2 address failed\n"); + +	regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC, +			   BT1_CCU_PCIE_DBI2_MODE, 0); +} + +static int bt1_pcie_start_link(struct dw_pcie *pci) +{ +	struct bt1_pcie *btpci = to_bt1_pcie(pci); +	u32 val; +	int ret; + +	/* +	 * Enable LTSSM and make sure it was able to establish both PHY and +	 * data links. This procedure shall work fine to reach 2.5 GT/s speed. +	 */ +	regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC, +			   BT1_CCU_PCIE_LTSSM_EN, BT1_CCU_PCIE_LTSSM_EN); + +	ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_PMSC, val, +				       (val & BT1_CCU_PCIE_SMLH_LINKUP), +				       BT1_PCIE_LNK_DELAY_US, BT1_PCIE_LNK_TIMEOUT_US); +	if (ret) { +		dev_err(pci->dev, "LTSSM failed to set PHY link up\n"); +		return ret; +	} + +	ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_PMSC, val, +				       (val & BT1_CCU_PCIE_RDLH_LINKUP), +				       BT1_PCIE_LNK_DELAY_US, BT1_PCIE_LNK_TIMEOUT_US); +	if (ret) { +		dev_err(pci->dev, "LTSSM failed to set data link up\n"); +		return ret; +	} + +	/* +	 * Activate direct speed change after the link is established in an +	 * attempt to reach a higher bus performance (up to Gen.3 - 8.0 GT/s). +	 * This is required at least to get 8.0 GT/s speed. +	 */ +	val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); +	val |= PORT_LOGIC_SPEED_CHANGE; +	dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); + +	ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_PMSC, val, +				       BT1_CCU_PCIE_LTSSM_LINKUP(val), +				       BT1_PCIE_LNK_DELAY_US, BT1_PCIE_LNK_TIMEOUT_US); +	if (ret) +		dev_err(pci->dev, "LTSSM failed to get into L0 state\n"); + +	return ret; +} + +static void bt1_pcie_stop_link(struct dw_pcie *pci) +{ +	struct bt1_pcie *btpci = to_bt1_pcie(pci); + +	regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC, +			   BT1_CCU_PCIE_LTSSM_EN, 0); +} + +static const struct dw_pcie_ops bt1_pcie_ops = { +	.read_dbi = bt1_pcie_read_dbi, +	.write_dbi = bt1_pcie_write_dbi, +	.write_dbi2 = bt1_pcie_write_dbi2, +	.start_link = bt1_pcie_start_link, +	.stop_link = bt1_pcie_stop_link, +}; + +static struct pci_ops bt1_pci_ops = { +	.map_bus = dw_pcie_own_conf_map_bus, +	.read = pci_generic_config_read32, +	.write = pci_generic_config_write32, +}; + +static int bt1_pcie_get_resources(struct bt1_pcie *btpci) +{ +	struct device *dev = btpci->dw.dev; +	int i; + +	/* DBI access is supposed to be performed by the dword-aligned IOs */ +	btpci->dw.pp.bridge->ops = &bt1_pci_ops; + +	/* These CSRs are in MMIO so we won't check the regmap-methods status */ +	btpci->sys_regs = +		syscon_regmap_lookup_by_phandle(dev->of_node, "baikal,bt1-syscon"); +	if (IS_ERR(btpci->sys_regs)) +		return dev_err_probe(dev, PTR_ERR(btpci->sys_regs), +				     "Failed to get syscon\n"); + +	/* Make sure all the required resources have been specified */ +	for (i = 0; i < BT1_PCIE_NUM_APP_CLKS; i++) { +		if (!btpci->dw.app_clks[bt1_pcie_app_clks[i]].clk) { +			dev_err(dev, "App clocks set is incomplete\n"); +			return -ENOENT; +		} +	} + +	for (i = 0; i < BT1_PCIE_NUM_CORE_CLKS; i++) { +		if (!btpci->dw.core_clks[bt1_pcie_core_clks[i]].clk) { +			dev_err(dev, "Core clocks set is incomplete\n"); +			return -ENOENT; +		} +	} + +	for (i = 0; i < BT1_PCIE_NUM_APP_RSTS; i++) { +		if (!btpci->dw.app_rsts[bt1_pcie_app_rsts[i]].rstc) { +			dev_err(dev, "App resets set is incomplete\n"); +			return -ENOENT; +		} +	} + +	for (i = 0; i < BT1_PCIE_NUM_CORE_RSTS; i++) { +		if (!btpci->dw.core_rsts[bt1_pcie_core_rsts[i]].rstc) { +			dev_err(dev, "Core resets set is incomplete\n"); +			return -ENOENT; +		} +	} + +	return 0; +} + +static void bt1_pcie_full_stop_bus(struct bt1_pcie *btpci, bool init) +{ +	struct device *dev = btpci->dw.dev; +	struct dw_pcie *pci = &btpci->dw; +	int ret; + +	/* Disable LTSSM for sure */ +	regmap_update_bits(btpci->sys_regs, BT1_CCU_PCIE_GENC, +			   BT1_CCU_PCIE_LTSSM_EN, 0); + +	/* +	 * Application reset controls are trigger-based so assert the core +	 * resets only. +	 */ +	ret = reset_control_bulk_assert(DW_PCIE_NUM_CORE_RSTS, pci->core_rsts); +	if (ret) +		dev_err(dev, "Failed to assert core resets\n"); + +	/* +	 * Clocks are disabled by default at least in accordance with the clk +	 * enable counter value on init stage. +	 */ +	if (!init) { +		clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, pci->core_clks); + +		clk_bulk_disable_unprepare(DW_PCIE_NUM_APP_CLKS, pci->app_clks); +	} + +	/* The peripheral devices are unavailable anyway so reset them too */ +	gpiod_set_value_cansleep(pci->pe_rst, 1); + +	/* Make sure all the resets are settled */ +	msleep(BT1_PCIE_RST_DELAY_MS); +} + +/* + * Implements the cold reset procedure in accordance with the reference manual + * and available PM signals. + */ +static int bt1_pcie_cold_start_bus(struct bt1_pcie *btpci) +{ +	struct device *dev = btpci->dw.dev; +	struct dw_pcie *pci = &btpci->dw; +	u32 val; +	int ret; + +	/* First get out of the Power/Hot reset state */ +	ret = reset_control_deassert(pci->core_rsts[DW_PCIE_PWR_RST].rstc); +	if (ret) { +		dev_err(dev, "Failed to deassert PHY reset\n"); +		return ret; +	} + +	ret = reset_control_deassert(pci->core_rsts[DW_PCIE_HOT_RST].rstc); +	if (ret) { +		dev_err(dev, "Failed to deassert hot reset\n"); +		goto err_assert_pwr_rst; +	} + +	/* Wait for the PM-core to stop requesting the PHY reset */ +	ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_RSTC, val, +				       !(val & BT1_CCU_PCIE_REQ_PHY_RST), +				       BT1_PCIE_REQ_DELAY_US, BT1_PCIE_REQ_TIMEOUT_US); +	if (ret) { +		dev_err(dev, "Timed out waiting for PM to stop PHY resetting\n"); +		goto err_assert_hot_rst; +	} + +	ret = reset_control_deassert(pci->core_rsts[DW_PCIE_PHY_RST].rstc); +	if (ret) { +		dev_err(dev, "Failed to deassert PHY reset\n"); +		goto err_assert_hot_rst; +	} + +	/* Clocks can be now enabled, but the ref one is crucial at this stage */ +	ret = clk_bulk_prepare_enable(DW_PCIE_NUM_APP_CLKS, pci->app_clks); +	if (ret) { +		dev_err(dev, "Failed to enable app clocks\n"); +		goto err_assert_phy_rst; +	} + +	ret = clk_bulk_prepare_enable(DW_PCIE_NUM_CORE_CLKS, pci->core_clks); +	if (ret) { +		dev_err(dev, "Failed to enable ref clocks\n"); +		goto err_disable_app_clk; +	} + +	/* Wait for the PM to stop requesting the controller core reset */ +	ret = regmap_read_poll_timeout(btpci->sys_regs, BT1_CCU_PCIE_RSTC, val, +				       !(val & BT1_CCU_PCIE_REQ_CORE_RST), +				       BT1_PCIE_REQ_DELAY_US, BT1_PCIE_REQ_TIMEOUT_US); +	if (ret) { +		dev_err(dev, "Timed out waiting for PM to stop core resetting\n"); +		goto err_disable_core_clk; +	} + +	/* PCS-PIPE interface and controller core can be now activated */ +	ret = reset_control_deassert(pci->core_rsts[DW_PCIE_PIPE_RST].rstc); +	if (ret) { +		dev_err(dev, "Failed to deassert PIPE reset\n"); +		goto err_disable_core_clk; +	} + +	ret = reset_control_deassert(pci->core_rsts[DW_PCIE_CORE_RST].rstc); +	if (ret) { +		dev_err(dev, "Failed to deassert core reset\n"); +		goto err_assert_pipe_rst; +	} + +	/* It's recommended to reset the core and application logic together */ +	ret = reset_control_bulk_reset(DW_PCIE_NUM_APP_RSTS, pci->app_rsts); +	if (ret) { +		dev_err(dev, "Failed to reset app domain\n"); +		goto err_assert_core_rst; +	} + +	/* Sticky/Non-sticky CSR flags can be now unreset too */ +	ret = reset_control_deassert(pci->core_rsts[DW_PCIE_STICKY_RST].rstc); +	if (ret) { +		dev_err(dev, "Failed to deassert sticky reset\n"); +		goto err_assert_core_rst; +	} + +	ret = reset_control_deassert(pci->core_rsts[DW_PCIE_NON_STICKY_RST].rstc); +	if (ret) { +		dev_err(dev, "Failed to deassert non-sticky reset\n"); +		goto err_assert_sticky_rst; +	} + +	/* Activate the PCIe bus peripheral devices */ +	gpiod_set_value_cansleep(pci->pe_rst, 0); + +	/* Make sure the state is settled (LTSSM is still disabled though) */ +	usleep_range(BT1_PCIE_RUN_DELAY_US, BT1_PCIE_RUN_DELAY_US + 100); + +	return 0; + +err_assert_sticky_rst: +	reset_control_assert(pci->core_rsts[DW_PCIE_STICKY_RST].rstc); + +err_assert_core_rst: +	reset_control_assert(pci->core_rsts[DW_PCIE_CORE_RST].rstc); + +err_assert_pipe_rst: +	reset_control_assert(pci->core_rsts[DW_PCIE_PIPE_RST].rstc); + +err_disable_core_clk: +	clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, pci->core_clks); + +err_disable_app_clk: +	clk_bulk_disable_unprepare(DW_PCIE_NUM_APP_CLKS, pci->app_clks); + +err_assert_phy_rst: +	reset_control_assert(pci->core_rsts[DW_PCIE_PHY_RST].rstc); + +err_assert_hot_rst: +	reset_control_assert(pci->core_rsts[DW_PCIE_HOT_RST].rstc); + +err_assert_pwr_rst: +	reset_control_assert(pci->core_rsts[DW_PCIE_PWR_RST].rstc); + +	return ret; +} + +static int bt1_pcie_host_init(struct dw_pcie_rp *pp) +{ +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp); +	struct bt1_pcie *btpci = to_bt1_pcie(pci); +	int ret; + +	ret = bt1_pcie_get_resources(btpci); +	if (ret) +		return ret; + +	bt1_pcie_full_stop_bus(btpci, true); + +	return bt1_pcie_cold_start_bus(btpci); +} + +static void bt1_pcie_host_deinit(struct dw_pcie_rp *pp) +{ +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp); +	struct bt1_pcie *btpci = to_bt1_pcie(pci); + +	bt1_pcie_full_stop_bus(btpci, false); +} + +static const struct dw_pcie_host_ops bt1_pcie_host_ops = { +	.host_init = bt1_pcie_host_init, +	.host_deinit = bt1_pcie_host_deinit, +}; + +static struct bt1_pcie *bt1_pcie_create_data(struct platform_device *pdev) +{ +	struct bt1_pcie *btpci; + +	btpci = devm_kzalloc(&pdev->dev, sizeof(*btpci), GFP_KERNEL); +	if (!btpci) +		return ERR_PTR(-ENOMEM); + +	btpci->pdev = pdev; + +	platform_set_drvdata(pdev, btpci); + +	return btpci; +} + +static int bt1_pcie_add_port(struct bt1_pcie *btpci) +{ +	struct device *dev = &btpci->pdev->dev; +	int ret; + +	btpci->dw.version = DW_PCIE_VER_460A; +	btpci->dw.dev = dev; +	btpci->dw.ops = &bt1_pcie_ops; + +	btpci->dw.pp.num_vectors = MAX_MSI_IRQS; +	btpci->dw.pp.ops = &bt1_pcie_host_ops; + +	dw_pcie_cap_set(&btpci->dw, REQ_RES); + +	ret = dw_pcie_host_init(&btpci->dw.pp); + +	return dev_err_probe(dev, ret, "Failed to initialize DWC PCIe host\n"); +} + +static void bt1_pcie_del_port(struct bt1_pcie *btpci) +{ +	dw_pcie_host_deinit(&btpci->dw.pp); +} + +static int bt1_pcie_probe(struct platform_device *pdev) +{ +	struct bt1_pcie *btpci; + +	btpci = bt1_pcie_create_data(pdev); +	if (IS_ERR(btpci)) +		return PTR_ERR(btpci); + +	return bt1_pcie_add_port(btpci); +} + +static int bt1_pcie_remove(struct platform_device *pdev) +{ +	struct bt1_pcie *btpci = platform_get_drvdata(pdev); + +	bt1_pcie_del_port(btpci); + +	return 0; +} + +static const struct of_device_id bt1_pcie_of_match[] = { +	{ .compatible = "baikal,bt1-pcie" }, +	{}, +}; +MODULE_DEVICE_TABLE(of, bt1_pcie_of_match); + +static struct platform_driver bt1_pcie_driver = { +	.probe = bt1_pcie_probe, +	.remove = bt1_pcie_remove, +	.driver = { +		.name	= "bt1-pcie", +		.of_match_table = bt1_pcie_of_match, +	}, +}; +module_platform_driver(bt1_pcie_driver); + +MODULE_AUTHOR("Serge Semin <[email protected]>"); +MODULE_DESCRIPTION("Baikal-T1 PCIe driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 83ddb190292e..d06654895eba 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -13,8 +13,6 @@  #include <linux/pci-epc.h>  #include <linux/pci-epf.h> -#include "../../pci.h" -  void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)  {  	struct pci_epc *epc = ep->epc; @@ -171,8 +169,8 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,  		return -EINVAL;  	} -	ret = dw_pcie_prog_inbound_atu(pci, func_no, free_win, type, -				       cpu_addr, bar); +	ret = dw_pcie_prog_ep_inbound_atu(pci, func_no, free_win, type, +					  cpu_addr, bar);  	if (ret < 0) {  		dev_err(pci->dev, "Failed to program IB window\n");  		return ret; @@ -643,7 +641,7 @@ static unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap)  int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)  {  	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); -	unsigned int offset; +	unsigned int offset, ptm_cap_base;  	unsigned int nbars;  	u8 hdr_type;  	u32 reg; @@ -659,6 +657,7 @@ int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)  	}  	offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR); +	ptm_cap_base = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_PTM);  	dw_pcie_dbi_ro_wr_en(pci); @@ -671,6 +670,22 @@ int dw_pcie_ep_init_complete(struct dw_pcie_ep *ep)  			dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, 0x0);  	} +	/* +	 * PTM responder capability can be disabled only after disabling +	 * PTM root capability. +	 */ +	if (ptm_cap_base) { +		dw_pcie_dbi_ro_wr_en(pci); +		reg = dw_pcie_readl_dbi(pci, ptm_cap_base + PCI_PTM_CAP); +		reg &= ~PCI_PTM_CAP_ROOT; +		dw_pcie_writel_dbi(pci, ptm_cap_base + PCI_PTM_CAP, reg); + +		reg = dw_pcie_readl_dbi(pci, ptm_cap_base + PCI_PTM_CAP); +		reg &= ~(PCI_PTM_CAP_RES | PCI_PTM_GRANULARITY_MASK); +		dw_pcie_writel_dbi(pci, ptm_cap_base + PCI_PTM_CAP, reg); +		dw_pcie_dbi_ro_wr_dis(pci); +	} +  	dw_pcie_setup(pci);  	dw_pcie_dbi_ro_wr_dis(pci); @@ -694,23 +709,9 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)  	INIT_LIST_HEAD(&ep->func_list); -	if (!pci->dbi_base) { -		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); -		pci->dbi_base = devm_pci_remap_cfg_resource(dev, res); -		if (IS_ERR(pci->dbi_base)) -			return PTR_ERR(pci->dbi_base); -	} - -	if (!pci->dbi_base2) { -		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2"); -		if (!res) { -			pci->dbi_base2 = pci->dbi_base + SZ_4K; -		} else { -			pci->dbi_base2 = devm_pci_remap_cfg_resource(dev, res); -			if (IS_ERR(pci->dbi_base2)) -				return PTR_ERR(pci->dbi_base2); -		} -	} +	ret = dw_pcie_get_resources(pci); +	if (ret) +		return ret;  	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");  	if (!res) @@ -739,9 +740,6 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)  		return -ENOMEM;  	ep->outbound_addr = addr; -	if (pci->link_gen < 1) -		pci->link_gen = of_pci_get_max_link_speed(np); -  	epc = devm_pci_epc_create(dev, &epc_ops);  	if (IS_ERR(epc)) {  		dev_err(dev, "Failed to create epc device\n"); diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 39f3b37d4033..3ab6ae3712c4 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -16,7 +16,6 @@  #include <linux/pci_regs.h>  #include <linux/platform_device.h> -#include "../../pci.h"  #include "pcie-designware.h"  static struct pci_ops dw_pcie_ops; @@ -395,6 +394,10 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)  	raw_spin_lock_init(&pp->lock); +	ret = dw_pcie_get_resources(pci); +	if (ret) +		return ret; +  	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");  	if (res) {  		pp->cfg0_size = resource_size(res); @@ -408,13 +411,6 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)  		return -ENODEV;  	} -	if (!pci->dbi_base) { -		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); -		pci->dbi_base = devm_pci_remap_cfg_resource(dev, res); -		if (IS_ERR(pci->dbi_base)) -			return PTR_ERR(pci->dbi_base); -	} -  	bridge = devm_pci_alloc_host_bridge(dev, 0);  	if (!bridge)  		return -ENOMEM; @@ -429,9 +425,6 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)  		pp->io_base = pci_pio_to_address(win->res->start);  	} -	if (pci->link_gen < 1) -		pci->link_gen = of_pci_get_max_link_speed(np); -  	/* Set default bus ops */  	bridge->ops = &dw_pcie_ops;  	bridge->child_ops = &dw_child_pcie_ops; @@ -643,12 +636,15 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)  	}  	/* -	 * Ensure all outbound windows are disabled before proceeding with -	 * the MEM/IO ranges setups. +	 * Ensure all out/inbound windows are disabled before proceeding with +	 * the MEM/IO (dma-)ranges setups.  	 */  	for (i = 0; i < pci->num_ob_windows; i++)  		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i); +	for (i = 0; i < pci->num_ib_windows; i++) +		dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, i); +  	i = 0;  	resource_list_for_each_entry(entry, &pp->bridge->windows) {  		if (resource_type(entry->res) != IORESOURCE_MEM) @@ -685,9 +681,32 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)  	}  	if (pci->num_ob_windows <= i) -		dev_warn(pci->dev, "Resources exceed number of ATU entries (%d)\n", +		dev_warn(pci->dev, "Ranges exceed outbound iATU size (%d)\n",  			 pci->num_ob_windows); +	i = 0; +	resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) { +		if (resource_type(entry->res) != IORESOURCE_MEM) +			continue; + +		if (pci->num_ib_windows <= i) +			break; + +		ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM, +					       entry->res->start, +					       entry->res->start - entry->offset, +					       resource_size(entry->res)); +		if (ret) { +			dev_err(pci->dev, "Failed to set DMA range %pr\n", +				entry->res); +			return ret; +		} +	} + +	if (pci->num_ib_windows <= i) +		dev_warn(pci->dev, "Dma-ranges exceed inbound iATU size (%u)\n", +			 pci->num_ib_windows); +  	return 0;  } diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index c6725c519a47..6d5d619ab2e9 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -10,7 +10,10 @@  #include <linux/align.h>  #include <linux/bitops.h> +#include <linux/clk.h>  #include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/ioport.h>  #include <linux/of.h>  #include <linux/of_platform.h>  #include <linux/sizes.h> @@ -19,6 +22,148 @@  #include "../../pci.h"  #include "pcie-designware.h" +static const char * const dw_pcie_app_clks[DW_PCIE_NUM_APP_CLKS] = { +	[DW_PCIE_DBI_CLK] = "dbi", +	[DW_PCIE_MSTR_CLK] = "mstr", +	[DW_PCIE_SLV_CLK] = "slv", +}; + +static const char * const dw_pcie_core_clks[DW_PCIE_NUM_CORE_CLKS] = { +	[DW_PCIE_PIPE_CLK] = "pipe", +	[DW_PCIE_CORE_CLK] = "core", +	[DW_PCIE_AUX_CLK] = "aux", +	[DW_PCIE_REF_CLK] = "ref", +}; + +static const char * const dw_pcie_app_rsts[DW_PCIE_NUM_APP_RSTS] = { +	[DW_PCIE_DBI_RST] = "dbi", +	[DW_PCIE_MSTR_RST] = "mstr", +	[DW_PCIE_SLV_RST] = "slv", +}; + +static const char * const dw_pcie_core_rsts[DW_PCIE_NUM_CORE_RSTS] = { +	[DW_PCIE_NON_STICKY_RST] = "non-sticky", +	[DW_PCIE_STICKY_RST] = "sticky", +	[DW_PCIE_CORE_RST] = "core", +	[DW_PCIE_PIPE_RST] = "pipe", +	[DW_PCIE_PHY_RST] = "phy", +	[DW_PCIE_HOT_RST] = "hot", +	[DW_PCIE_PWR_RST] = "pwr", +}; + +static int dw_pcie_get_clocks(struct dw_pcie *pci) +{ +	int i, ret; + +	for (i = 0; i < DW_PCIE_NUM_APP_CLKS; i++) +		pci->app_clks[i].id = dw_pcie_app_clks[i]; + +	for (i = 0; i < DW_PCIE_NUM_CORE_CLKS; i++) +		pci->core_clks[i].id = dw_pcie_core_clks[i]; + +	ret = devm_clk_bulk_get_optional(pci->dev, DW_PCIE_NUM_APP_CLKS, +					 pci->app_clks); +	if (ret) +		return ret; + +	return devm_clk_bulk_get_optional(pci->dev, DW_PCIE_NUM_CORE_CLKS, +					  pci->core_clks); +} + +static int dw_pcie_get_resets(struct dw_pcie *pci) +{ +	int i, ret; + +	for (i = 0; i < DW_PCIE_NUM_APP_RSTS; i++) +		pci->app_rsts[i].id = dw_pcie_app_rsts[i]; + +	for (i = 0; i < DW_PCIE_NUM_CORE_RSTS; i++) +		pci->core_rsts[i].id = dw_pcie_core_rsts[i]; + +	ret = devm_reset_control_bulk_get_optional_shared(pci->dev, +							  DW_PCIE_NUM_APP_RSTS, +							  pci->app_rsts); +	if (ret) +		return ret; + +	ret = devm_reset_control_bulk_get_optional_exclusive(pci->dev, +							     DW_PCIE_NUM_CORE_RSTS, +							     pci->core_rsts); +	if (ret) +		return ret; + +	pci->pe_rst = devm_gpiod_get_optional(pci->dev, "reset", GPIOD_OUT_HIGH); +	if (IS_ERR(pci->pe_rst)) +		return PTR_ERR(pci->pe_rst); + +	return 0; +} + +int dw_pcie_get_resources(struct dw_pcie *pci) +{ +	struct platform_device *pdev = to_platform_device(pci->dev); +	struct device_node *np = dev_of_node(pci->dev); +	struct resource *res; +	int ret; + +	if (!pci->dbi_base) { +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); +		pci->dbi_base = devm_pci_remap_cfg_resource(pci->dev, res); +		if (IS_ERR(pci->dbi_base)) +			return PTR_ERR(pci->dbi_base); +	} + +	/* DBI2 is mainly useful for the endpoint controller */ +	if (!pci->dbi_base2) { +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi2"); +		if (res) { +			pci->dbi_base2 = devm_pci_remap_cfg_resource(pci->dev, res); +			if (IS_ERR(pci->dbi_base2)) +				return PTR_ERR(pci->dbi_base2); +		} else { +			pci->dbi_base2 = pci->dbi_base + SZ_4K; +		} +	} + +	/* For non-unrolled iATU/eDMA platforms this range will be ignored */ +	if (!pci->atu_base) { +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "atu"); +		if (res) { +			pci->atu_size = resource_size(res); +			pci->atu_base = devm_ioremap_resource(pci->dev, res); +			if (IS_ERR(pci->atu_base)) +				return PTR_ERR(pci->atu_base); +		} else { +			pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET; +		} +	} + +	/* Set a default value suitable for at most 8 in and 8 out windows */ +	if (!pci->atu_size) +		pci->atu_size = SZ_4K; + +	/* LLDD is supposed to manually switch the clocks and resets state */ +	if (dw_pcie_cap_is(pci, REQ_RES)) { +		ret = dw_pcie_get_clocks(pci); +		if (ret) +			return ret; + +		ret = dw_pcie_get_resets(pci); +		if (ret) +			return ret; +	} + +	if (pci->link_gen < 1) +		pci->link_gen = of_pci_get_max_link_speed(np); + +	of_property_read_u32(np, "num-lanes", &pci->num_lanes); + +	if (of_property_read_bool(np, "snps,enable-cdm-check")) +		dw_pcie_cap_set(pci, CDM_CHECK); + +	return 0; +} +  void dw_pcie_version_detect(struct dw_pcie *pci)  {  	u32 ver; @@ -211,7 +356,7 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val)  static inline void __iomem *dw_pcie_select_atu(struct dw_pcie *pci, u32 dir,  					       u32 index)  { -	if (pci->iatu_unroll_enabled) +	if (dw_pcie_cap_is(pci, IATU_UNROLL))  		return pci->atu_base + PCIE_ATU_UNROLL_BASE(dir, index);  	dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, dir | index); @@ -393,8 +538,60 @@ static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg  	dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);  } -int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index, -			     int type, u64 cpu_addr, u8 bar) +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type, +			     u64 cpu_addr, u64 pci_addr, u64 size) +{ +	u64 limit_addr = pci_addr + size - 1; +	u32 retries, val; + +	if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) || +	    !IS_ALIGNED(cpu_addr, pci->region_align) || +	    !IS_ALIGNED(pci_addr, pci->region_align) || !size) { +		return -EINVAL; +	} + +	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_BASE, +			      lower_32_bits(pci_addr)); +	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_BASE, +			      upper_32_bits(pci_addr)); + +	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LIMIT, +			      lower_32_bits(limit_addr)); +	if (dw_pcie_ver_is_ge(pci, 460A)) +		dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_LIMIT, +				      upper_32_bits(limit_addr)); + +	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET, +			      lower_32_bits(cpu_addr)); +	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET, +			      upper_32_bits(cpu_addr)); + +	val = type; +	if (upper_32_bits(limit_addr) > upper_32_bits(pci_addr) && +	    dw_pcie_ver_is_ge(pci, 460A)) +		val |= PCIE_ATU_INCREASE_REGION_SIZE; +	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1, val); +	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2, PCIE_ATU_ENABLE); + +	/* +	 * Make sure ATU enable takes effect before any subsequent config +	 * and I/O accesses. +	 */ +	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { +		val = dw_pcie_readl_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2); +		if (val & PCIE_ATU_ENABLE) +			return 0; + +		mdelay(LINK_WAIT_IATU); +	} + +	dev_err(pci->dev, "Inbound iATU is not being enabled\n"); + +	return -ETIMEDOUT; +} + +int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index, +				int type, u64 cpu_addr, u8 bar)  {  	u32 retries, val; @@ -448,7 +645,7 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci)  	}  	if (retries >= LINK_WAIT_MAX_RETRIES) { -		dev_err(pci->dev, "Phy link never came up\n"); +		dev_info(pci->dev, "Phy link never came up\n");  		return -ETIMEDOUT;  	} @@ -522,26 +719,21 @@ static void dw_pcie_link_set_max_speed(struct dw_pcie *pci, u32 link_gen)  } -static bool dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci) -{ -	u32 val; - -	val = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT); -	if (val == 0xffffffff) -		return true; - -	return false; -} - -static void dw_pcie_iatu_detect_regions(struct dw_pcie *pci) +void dw_pcie_iatu_detect(struct dw_pcie *pci)  {  	int max_region, ob, ib;  	u32 val, min, dir;  	u64 max; -	if (pci->iatu_unroll_enabled) { +	val = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT); +	if (val == 0xFFFFFFFF) { +		dw_pcie_cap_set(pci, IATU_UNROLL); +  		max_region = min((int)pci->atu_size / 512, 256);  	} else { +		pci->atu_base = pci->dbi_base + PCIE_ATU_VIEWPORT_BASE; +		pci->atu_size = PCIE_ATU_VIEWPORT_SIZE; +  		dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, 0xFF);  		max_region = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT) + 1;  	} @@ -583,46 +775,15 @@ static void dw_pcie_iatu_detect_regions(struct dw_pcie *pci)  	pci->num_ib_windows = ib;  	pci->region_align = 1 << fls(min);  	pci->region_limit = (max << 32) | (SZ_4G - 1); -} -void dw_pcie_iatu_detect(struct dw_pcie *pci) -{ -	struct platform_device *pdev = to_platform_device(pci->dev); - -	pci->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pci); -	if (pci->iatu_unroll_enabled) { -		if (!pci->atu_base) { -			struct resource *res = -				platform_get_resource_byname(pdev, IORESOURCE_MEM, "atu"); -			if (res) { -				pci->atu_size = resource_size(res); -				pci->atu_base = devm_ioremap_resource(pci->dev, res); -			} -			if (!pci->atu_base || IS_ERR(pci->atu_base)) -				pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET; -		} - -		if (!pci->atu_size) -			/* Pick a minimal default, enough for 8 in and 8 out windows */ -			pci->atu_size = SZ_4K; -	} else { -		pci->atu_base = pci->dbi_base + PCIE_ATU_VIEWPORT_BASE; -		pci->atu_size = PCIE_ATU_VIEWPORT_SIZE; -	} - -	dw_pcie_iatu_detect_regions(pci); - -	dev_info(pci->dev, "iATU unroll: %s\n", pci->iatu_unroll_enabled ? -		"enabled" : "disabled"); - -	dev_info(pci->dev, "iATU regions: %u ob, %u ib, align %uK, limit %lluG\n", +	dev_info(pci->dev, "iATU: unroll %s, %u ob, %u ib, align %uK, limit %lluG\n", +		 dw_pcie_cap_is(pci, IATU_UNROLL) ? "T" : "F",  		 pci->num_ob_windows, pci->num_ib_windows,  		 pci->region_align / SZ_1K, (pci->region_limit + 1) / SZ_1G);  }  void dw_pcie_setup(struct dw_pcie *pci)  { -	struct device_node *np = pci->dev->of_node;  	u32 val;  	if (pci->link_gen > 0) @@ -641,7 +802,7 @@ void dw_pcie_setup(struct dw_pcie *pci)  	if (pci->n_fts[1]) {  		val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);  		val &= ~PORT_LOGIC_N_FTS_MASK; -		val |= pci->n_fts[pci->link_gen - 1]; +		val |= pci->n_fts[1];  		dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);  	} @@ -650,14 +811,13 @@ void dw_pcie_setup(struct dw_pcie *pci)  	val |= PORT_LINK_DLL_LINK_EN;  	dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val); -	if (of_property_read_bool(np, "snps,enable-cdm-check")) { +	if (dw_pcie_cap_is(pci, CDM_CHECK)) {  		val = dw_pcie_readl_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS);  		val |= PCIE_PL_CHK_REG_CHK_REG_CONTINUOUS |  		       PCIE_PL_CHK_REG_CHK_REG_START;  		dw_pcie_writel_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS, val);  	} -	of_property_read_u32(np, "num-lanes", &pci->num_lanes);  	if (!pci->num_lanes) {  		dev_dbg(pci->dev, "Using h/w default number of lanes\n");  		return; diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index a871ae7eb59e..393dfb931df6 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -12,10 +12,14 @@  #define _PCIE_DESIGNWARE_H  #include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/clk.h>  #include <linux/dma-mapping.h> +#include <linux/gpio/consumer.h>  #include <linux/irq.h>  #include <linux/msi.h>  #include <linux/pci.h> +#include <linux/reset.h>  #include <linux/pci-epc.h>  #include <linux/pci-epf.h> @@ -43,6 +47,17 @@  	(__dw_pcie_ver_cmp(_pci, _ver, ==) && \  	 __dw_pcie_ver_cmp(_pci, TYPE_ ## _type, >=)) +/* DWC PCIe controller capabilities */ +#define DW_PCIE_CAP_REQ_RES		0 +#define DW_PCIE_CAP_IATU_UNROLL		1 +#define DW_PCIE_CAP_CDM_CHECK		2 + +#define dw_pcie_cap_is(_pci, _cap) \ +	test_bit(DW_PCIE_CAP_ ## _cap, &(_pci)->caps) + +#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_USLEEP_MIN		90000 @@ -222,6 +237,39 @@ enum dw_pcie_device_mode {  	DW_PCIE_RC_TYPE,  }; +enum dw_pcie_app_clk { +	DW_PCIE_DBI_CLK, +	DW_PCIE_MSTR_CLK, +	DW_PCIE_SLV_CLK, +	DW_PCIE_NUM_APP_CLKS +}; + +enum dw_pcie_core_clk { +	DW_PCIE_PIPE_CLK, +	DW_PCIE_CORE_CLK, +	DW_PCIE_AUX_CLK, +	DW_PCIE_REF_CLK, +	DW_PCIE_NUM_CORE_CLKS +}; + +enum dw_pcie_app_rst { +	DW_PCIE_DBI_RST, +	DW_PCIE_MSTR_RST, +	DW_PCIE_SLV_RST, +	DW_PCIE_NUM_APP_RSTS +}; + +enum dw_pcie_core_rst { +	DW_PCIE_NON_STICKY_RST, +	DW_PCIE_STICKY_RST, +	DW_PCIE_CORE_RST, +	DW_PCIE_PIPE_RST, +	DW_PCIE_PHY_RST, +	DW_PCIE_HOT_RST, +	DW_PCIE_PWR_RST, +	DW_PCIE_NUM_CORE_RSTS +}; +  struct dw_pcie_host_ops {  	int (*host_init)(struct dw_pcie_rp *pp);  	void (*host_deinit)(struct dw_pcie_rp *pp); @@ -317,10 +365,15 @@ struct dw_pcie {  	const struct dw_pcie_ops *ops;  	u32			version;  	u32			type; +	unsigned long		caps;  	int			num_lanes;  	int			link_gen;  	u8			n_fts[2]; -	bool			iatu_unroll_enabled: 1; +	struct clk_bulk_data	app_clks[DW_PCIE_NUM_APP_CLKS]; +	struct clk_bulk_data	core_clks[DW_PCIE_NUM_CORE_CLKS]; +	struct reset_control_bulk_data	app_rsts[DW_PCIE_NUM_APP_RSTS]; +	struct reset_control_bulk_data	core_rsts[DW_PCIE_NUM_CORE_RSTS]; +	struct gpio_desc		*pe_rst;  };  #define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp) @@ -328,6 +381,8 @@ struct dw_pcie {  #define to_dw_pcie_from_ep(endpoint)   \  		container_of((endpoint), struct dw_pcie, ep) +int dw_pcie_get_resources(struct dw_pcie *pci); +  void dw_pcie_version_detect(struct dw_pcie *pci);  u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap); @@ -346,8 +401,10 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,  			      u64 cpu_addr, u64 pci_addr, u64 size);  int dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,  				 int type, u64 cpu_addr, u64 pci_addr, u64 size); -int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index, -			     int type, u64 cpu_addr, u8 bar); +int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type, +			     u64 cpu_addr, u64 pci_addr, u64 size); +int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index, +				int type, u64 cpu_addr, u8 bar);  void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);  void dw_pcie_setup(struct dw_pcie *pci);  void dw_pcie_iatu_detect(struct dw_pcie *pci); diff --git a/drivers/pci/controller/dwc/pcie-histb.c b/drivers/pci/controller/dwc/pcie-histb.c index e2b80f10030d..43c27812dd6d 100644 --- a/drivers/pci/controller/dwc/pcie-histb.c +++ b/drivers/pci/controller/dwc/pcie-histb.c @@ -10,11 +10,11 @@  #include <linux/clk.h>  #include <linux/delay.h> +#include <linux/gpio/consumer.h>  #include <linux/interrupt.h>  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/of.h> -#include <linux/of_gpio.h>  #include <linux/pci.h>  #include <linux/phy/phy.h>  #include <linux/platform_device.h> @@ -60,7 +60,7 @@ struct histb_pcie {  	struct reset_control *sys_reset;  	struct reset_control *bus_reset;  	void __iomem *ctrl; -	int reset_gpio; +	struct gpio_desc *reset_gpio;  	struct regulator *vpcie;  }; @@ -212,8 +212,8 @@ static void histb_pcie_host_disable(struct histb_pcie *hipcie)  	clk_disable_unprepare(hipcie->sys_clk);  	clk_disable_unprepare(hipcie->bus_clk); -	if (gpio_is_valid(hipcie->reset_gpio)) -		gpio_set_value_cansleep(hipcie->reset_gpio, 0); +	if (hipcie->reset_gpio) +		gpiod_set_value_cansleep(hipcie->reset_gpio, 1);  	if (hipcie->vpcie)  		regulator_disable(hipcie->vpcie); @@ -235,8 +235,8 @@ static int histb_pcie_host_enable(struct dw_pcie_rp *pp)  		}  	} -	if (gpio_is_valid(hipcie->reset_gpio)) -		gpio_set_value_cansleep(hipcie->reset_gpio, 1); +	if (hipcie->reset_gpio) +		gpiod_set_value_cansleep(hipcie->reset_gpio, 0);  	ret = clk_prepare_enable(hipcie->bus_clk);  	if (ret) { @@ -298,10 +298,7 @@ static int histb_pcie_probe(struct platform_device *pdev)  	struct histb_pcie *hipcie;  	struct dw_pcie *pci;  	struct dw_pcie_rp *pp; -	struct device_node *np = pdev->dev.of_node;  	struct device *dev = &pdev->dev; -	enum of_gpio_flags of_flags; -	unsigned long flag = GPIOF_DIR_OUT;  	int ret;  	hipcie = devm_kzalloc(dev, sizeof(*hipcie), GFP_KERNEL); @@ -336,17 +333,19 @@ static int histb_pcie_probe(struct platform_device *pdev)  		hipcie->vpcie = NULL;  	} -	hipcie->reset_gpio = of_get_named_gpio_flags(np, -				"reset-gpios", 0, &of_flags); -	if (of_flags & OF_GPIO_ACTIVE_LOW) -		flag |= GPIOF_ACTIVE_LOW; -	if (gpio_is_valid(hipcie->reset_gpio)) { -		ret = devm_gpio_request_one(dev, hipcie->reset_gpio, -				flag, "PCIe device power control"); -		if (ret) { -			dev_err(dev, "unable to request gpio\n"); -			return ret; -		} +	hipcie->reset_gpio = devm_gpiod_get_optional(dev, "reset", +						     GPIOD_OUT_HIGH); +	ret = PTR_ERR_OR_ZERO(hipcie->reset_gpio); +	if (ret) { +		dev_err(dev, "unable to request reset gpio: %d\n", ret); +		return ret; +	} + +	ret = gpiod_set_consumer_name(hipcie->reset_gpio, +				      "PCIe device power control"); +	if (ret) { +		dev_err(dev, "unable to set reset gpio name: %d\n", ret); +		return ret;  	}  	hipcie->aux_clk = devm_clk_get(dev, "aux"); diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index 6d0d1b759ca2..19b32839ea26 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -14,6 +14,7 @@  #include <linux/delay.h>  #include <linux/gpio/consumer.h>  #include <linux/mfd/syscon.h> +#include <linux/phy/pcie.h>  #include <linux/phy/phy.h>  #include <linux/platform_device.h>  #include <linux/pm_domain.h> @@ -268,6 +269,10 @@ static int qcom_pcie_enable_resources(struct qcom_pcie_ep *pcie_ep)  	if (ret)  		goto err_disable_clk; +	ret = phy_set_mode_ext(pcie_ep->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_EP); +	if (ret) +		goto err_phy_exit; +  	ret = phy_power_on(pcie_ep->phy);  	if (ret)  		goto err_phy_exit; diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index f711acacaeaf..77e5dc7b88ad 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -12,6 +12,7 @@  #include <linux/crc8.h>  #include <linux/delay.h>  #include <linux/gpio/consumer.h> +#include <linux/interconnect.h>  #include <linux/interrupt.h>  #include <linux/io.h>  #include <linux/iopoll.h> @@ -22,6 +23,7 @@  #include <linux/pci.h>  #include <linux/pm_runtime.h>  #include <linux/platform_device.h> +#include <linux/phy/pcie.h>  #include <linux/phy/phy.h>  #include <linux/regulator/consumer.h>  #include <linux/reset.h> @@ -223,6 +225,7 @@ struct qcom_pcie {  	union qcom_pcie_resources res;  	struct phy *phy;  	struct gpio_desc *reset; +	struct icc_path *icc_mem;  	const struct qcom_pcie_cfg *cfg;  }; @@ -1236,7 +1239,7 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie)  	ret = reset_control_assert(res->pci_reset);  	if (ret < 0) { -		dev_err(dev, "cannot deassert pci reset\n"); +		dev_err(dev, "cannot assert pci reset\n");  		goto err_disable_clocks;  	} @@ -1497,6 +1500,10 @@ 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);  	if (ret)  		goto err_deinit; @@ -1639,6 +1646,74 @@ static const struct dw_pcie_ops dw_pcie_ops = {  	.start_link = qcom_pcie_start_link,  }; +static int qcom_pcie_icc_init(struct qcom_pcie *pcie) +{ +	struct dw_pcie *pci = pcie->pci; +	int ret; + +	pcie->icc_mem = devm_of_icc_get(pci->dev, "pcie-mem"); +	if (IS_ERR(pcie->icc_mem)) +		return PTR_ERR(pcie->icc_mem); + +	/* +	 * Some Qualcomm platforms require interconnect bandwidth constraints +	 * to be set before enabling interconnect clocks. +	 * +	 * Set an initial peak bandwidth corresponding to single-lane Gen 1 +	 * for the pcie-mem path. +	 */ +	ret = icc_set_bw(pcie->icc_mem, 0, MBps_to_icc(250)); +	if (ret) { +		dev_err(pci->dev, "failed to set interconnect bandwidth: %d\n", +			ret); +		return ret; +	} + +	return 0; +} + +static void qcom_pcie_icc_update(struct qcom_pcie *pcie) +{ +	struct dw_pcie *pci = pcie->pci; +	u32 offset, status, bw; +	int speed, width; +	int ret; + +	if (!pcie->icc_mem) +		return; + +	offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); +	status = readw(pci->dbi_base + offset + PCI_EXP_LNKSTA); + +	/* Only update constraints if link is up. */ +	if (!(status & PCI_EXP_LNKSTA_DLLLA)) +		return; + +	speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, status); +	width = FIELD_GET(PCI_EXP_LNKSTA_NLW, status); + +	switch (speed) { +	case 1: +		bw = MBps_to_icc(250); +		break; +	case 2: +		bw = MBps_to_icc(500); +		break; +	default: +		WARN_ON_ONCE(1); +		fallthrough; +	case 3: +		bw = MBps_to_icc(985); +		break; +	} + +	ret = icc_set_bw(pcie->icc_mem, 0, width * bw); +	if (ret) { +		dev_err(pci->dev, "failed to set interconnect bandwidth: %d\n", +			ret); +	} +} +  static int qcom_pcie_probe(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev; @@ -1699,6 +1774,10 @@ static int qcom_pcie_probe(struct platform_device *pdev)  		goto err_pm_runtime_put;  	} +	ret = qcom_pcie_icc_init(pcie); +	if (ret) +		goto err_pm_runtime_put; +  	ret = pcie->cfg->ops->get_resources(pcie);  	if (ret)  		goto err_pm_runtime_put; @@ -1717,6 +1796,8 @@ static int qcom_pcie_probe(struct platform_device *pdev)  		goto err_phy_exit;  	} +	qcom_pcie_icc_update(pcie); +  	return 0;  err_phy_exit: diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 1b6b437823d2..02d78a12b6e7 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -21,7 +21,6 @@  #include <linux/of.h>  #include <linux/of_device.h>  #include <linux/of_gpio.h> -#include <linux/of_irq.h>  #include <linux/of_pci.h>  #include <linux/pci.h>  #include <linux/phy/phy.h> |