Commit 531abff0 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/controller/qcom'

- Select PCI Power Control Slot driver so slot voltage rails can be turned
  on/off if described in Root Port device tree node (Qiang Yu)

- Parse only PCI bridge child nodes in device tree, skipping unrelated
  nodes such as OPP (Operating Performance Points), which caused probe
  failures (Krishna Chaitanya Chundru)

- Add 8.0 GT/s and 32.0 GT/s equalization settings (Ziyue Zhang)

- Fix typo in CURSOR macro names (Ziyue Zhang)

- Consolidate Root Port 'phy' and 'reset' properties in struct
  qcom_pcie_port, regardless of whether we got them from the Root Port node
  or the host bridge node (Manivannan Sadhasivam)

- Fetch and map the ELBI register space in the DWC core rather than in each
  driver individually (Krishna Chaitanya Chundru)

- Enable ECAM mechanism in DWC core by setting up iATU with 'CFG Shift
  Feature' and use this in the qcom driver (Krishna Chaitanya Chundru)

* pci/controller/qcom:
  PCI: dwc: Support ECAM mechanism by enabling iATU 'CFG Shift Feature'
  PCI: qcom: Prepare for the DWC ECAM enablement
  PCI: dwc: Prepare the driver for enabling ECAM mechanism using iATU 'CFG Shift Feature'
  PCI: dwc: Add support for ELBI resource mapping
  PCI: qcom: Move host bridge 'phy' and 'reset' pointers to struct qcom_pcie_port
  PCI: qcom: Fix macro typo for CURSOR
  PCI: qcom: Add equalization settings for 8.0 GT/s and 32.0 GT/s
  PCI: qcom: Restrict port parsing only to PCIe bridge child nodes
  PCI: qcom: Select PCI Power Control Slot driver
parents 93f32da3 0da48c5b
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ config PCIE_DW_HOST
	bool
	select PCIE_DW
	select IRQ_MSI_LIB
	select PCI_HOST_COMMON

config PCIE_DW_EP
	bool
@@ -298,6 +299,7 @@ config PCIE_QCOM
	select CRC8
	select PCIE_QCOM_COMMON
	select PCI_HOST_COMMON
	select PCI_PWRCTRL_SLOT
	help
	  Say Y here to enable PCIe controller support on Qualcomm SoCs. The
	  PCIe controller uses the DesignWare core plus Qualcomm-specific
+31 −31
Original line number Diff line number Diff line
@@ -53,7 +53,6 @@

struct exynos_pcie {
	struct dw_pcie			pci;
	void __iomem			*elbi_base;
	struct clk_bulk_data		*clks;
	struct phy			*phy;
	struct regulator_bulk_data	supplies[2];
@@ -71,73 +70,78 @@ static u32 exynos_pcie_readl(void __iomem *base, u32 reg)

static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on)
{
	struct dw_pcie *pci = &ep->pci;
	u32 val;

	val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_AWMISC);
	val = exynos_pcie_readl(pci->elbi_base, PCIE_ELBI_SLV_AWMISC);
	if (on)
		val |= PCIE_ELBI_SLV_DBI_ENABLE;
	else
		val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
	exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
	exynos_pcie_writel(pci->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
}

static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on)
{
	struct dw_pcie *pci = &ep->pci;
	u32 val;

	val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_ARMISC);
	val = exynos_pcie_readl(pci->elbi_base, PCIE_ELBI_SLV_ARMISC);
	if (on)
		val |= PCIE_ELBI_SLV_DBI_ENABLE;
	else
		val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
	exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
	exynos_pcie_writel(pci->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
}

static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep)
{
	struct dw_pcie *pci = &ep->pci;
	u32 val;

	val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
	val = exynos_pcie_readl(pci->elbi_base, PCIE_CORE_RESET);
	val &= ~PCIE_CORE_RESET_ENABLE;
	exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
	exynos_pcie_writel(ep->elbi_base, 0, PCIE_STICKY_RESET);
	exynos_pcie_writel(ep->elbi_base, 0, PCIE_NONSTICKY_RESET);
	exynos_pcie_writel(pci->elbi_base, val, PCIE_CORE_RESET);
	exynos_pcie_writel(pci->elbi_base, 0, PCIE_STICKY_RESET);
	exynos_pcie_writel(pci->elbi_base, 0, PCIE_NONSTICKY_RESET);
}

static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep)
{
	struct dw_pcie *pci = &ep->pci;
	u32 val;

	val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
	val = exynos_pcie_readl(pci->elbi_base, PCIE_CORE_RESET);
	val |= PCIE_CORE_RESET_ENABLE;

	exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
	exynos_pcie_writel(ep->elbi_base, 1, PCIE_STICKY_RESET);
	exynos_pcie_writel(ep->elbi_base, 1, PCIE_NONSTICKY_RESET);
	exynos_pcie_writel(ep->elbi_base, 1, PCIE_APP_INIT_RESET);
	exynos_pcie_writel(ep->elbi_base, 0, PCIE_APP_INIT_RESET);
	exynos_pcie_writel(pci->elbi_base, val, PCIE_CORE_RESET);
	exynos_pcie_writel(pci->elbi_base, 1, PCIE_STICKY_RESET);
	exynos_pcie_writel(pci->elbi_base, 1, PCIE_NONSTICKY_RESET);
	exynos_pcie_writel(pci->elbi_base, 1, PCIE_APP_INIT_RESET);
	exynos_pcie_writel(pci->elbi_base, 0, PCIE_APP_INIT_RESET);
}

static int exynos_pcie_start_link(struct dw_pcie *pci)
{
	struct exynos_pcie *ep = to_exynos_pcie(pci);
	u32 val;

	val = exynos_pcie_readl(ep->elbi_base, PCIE_SW_WAKE);
	val = exynos_pcie_readl(pci->elbi_base, PCIE_SW_WAKE);
	val &= ~PCIE_BUS_EN;
	exynos_pcie_writel(ep->elbi_base, val, PCIE_SW_WAKE);
	exynos_pcie_writel(pci->elbi_base, val, PCIE_SW_WAKE);

	/* assert LTSSM enable */
	exynos_pcie_writel(ep->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
	exynos_pcie_writel(pci->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
			  PCIE_APP_LTSSM_ENABLE);
	return 0;
}

static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep)
{
	u32 val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_PULSE);
	struct dw_pcie *pci = &ep->pci;

	exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_PULSE);
	u32 val = exynos_pcie_readl(pci->elbi_base, PCIE_IRQ_PULSE);

	exynos_pcie_writel(pci->elbi_base, val, PCIE_IRQ_PULSE);
}

static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
@@ -150,12 +154,14 @@ static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)

static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
{
	struct dw_pcie *pci = &ep->pci;

	u32 val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
		  IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;

	exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_PULSE);
	exynos_pcie_writel(ep->elbi_base, 0, PCIE_IRQ_EN_LEVEL);
	exynos_pcie_writel(ep->elbi_base, 0, PCIE_IRQ_EN_SPECIAL);
	exynos_pcie_writel(pci->elbi_base, val, PCIE_IRQ_EN_PULSE);
	exynos_pcie_writel(pci->elbi_base, 0, PCIE_IRQ_EN_LEVEL);
	exynos_pcie_writel(pci->elbi_base, 0, PCIE_IRQ_EN_SPECIAL);
}

static u32 exynos_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base,
@@ -211,8 +217,7 @@ static struct pci_ops exynos_pci_ops = {

static bool exynos_pcie_link_up(struct dw_pcie *pci)
{
	struct exynos_pcie *ep = to_exynos_pcie(pci);
	u32 val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_RDLH_LINKUP);
	u32 val = exynos_pcie_readl(pci->elbi_base, PCIE_ELBI_RDLH_LINKUP);

	return val & PCIE_ELBI_XMLH_LINKUP;
}
@@ -295,11 +300,6 @@ static int exynos_pcie_probe(struct platform_device *pdev)
	if (IS_ERR(ep->phy))
		return PTR_ERR(ep->phy);

	/* External Local Bus interface (ELBI) registers */
	ep->elbi_base = devm_platform_ioremap_resource_byname(pdev, "elbi");
	if (IS_ERR(ep->elbi_base))
		return PTR_ERR(ep->elbi_base);

	ret = devm_clk_bulk_get_all_enabled(dev, &ep->clks);
	if (ret < 0)
		return ret;
+1 −0
Original line number Diff line number Diff line
@@ -352,6 +352,7 @@ static int al_pcie_probe(struct platform_device *pdev)
		return -ENOENT;
	}
	al_pcie->ecam_size = resource_size(ecam_res);
	pci->pp.native_ecam = true;

	controller_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
						      "controller");
+134 −14
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
 * Author: Jingoo Han <jg1.han@samsung.com>
 */

#include <linux/align.h>
#include <linux/iopoll.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/irq-msi-lib.h>
@@ -32,6 +33,8 @@ static struct pci_ops dw_child_pcie_ops;
				     MSI_FLAG_PCI_MSIX			| \
				     MSI_GENERIC_FLAGS_MASK)

#define IS_256MB_ALIGNED(x) IS_ALIGNED(x, SZ_256M)

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,
@@ -413,6 +416,95 @@ static void dw_pcie_host_request_msg_tlp_res(struct dw_pcie_rp *pp)
	}
}

static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp)
{
	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
	struct dw_pcie_ob_atu_cfg atu = {0};
	resource_size_t bus_range_max;
	struct resource_entry *bus;
	int ret;

	bus = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS);

	/*
	 * Root bus under the host bridge doesn't require any iATU configuration
	 * as DBI region will be used to access root bus config space.
	 * Immediate bus under Root Bus, needs type 0 iATU configuration and
	 * remaining buses need type 1 iATU configuration.
	 */
	atu.index = 0;
	atu.type = PCIE_ATU_TYPE_CFG0;
	atu.parent_bus_addr = pp->cfg0_base + SZ_1M;
	/* 1MiB is to cover 1 (bus) * 32 (devices) * 8 (functions) */
	atu.size = SZ_1M;
	atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE;
	ret = dw_pcie_prog_outbound_atu(pci, &atu);
	if (ret)
		return ret;

	bus_range_max = resource_size(bus->res);

	if (bus_range_max < 2)
		return 0;

	/* Configure remaining buses in type 1 iATU configuration */
	atu.index = 1;
	atu.type = PCIE_ATU_TYPE_CFG1;
	atu.parent_bus_addr = pp->cfg0_base + SZ_2M;
	atu.size = (SZ_1M * bus_range_max) - SZ_2M;
	atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE;

	return dw_pcie_prog_outbound_atu(pci, &atu);
}

static int dw_pcie_create_ecam_window(struct dw_pcie_rp *pp, struct resource *res)
{
	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
	struct device *dev = pci->dev;
	struct resource_entry *bus;

	bus = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS);
	if (!bus)
		return -ENODEV;

	pp->cfg = pci_ecam_create(dev, res, bus->res, &pci_generic_ecam_ops);
	if (IS_ERR(pp->cfg))
		return PTR_ERR(pp->cfg);

	pci->dbi_base = pp->cfg->win;
	pci->dbi_phys_addr = res->start;

	return 0;
}

static bool dw_pcie_ecam_enabled(struct dw_pcie_rp *pp, struct resource *config_res)
{
	struct resource *bus_range;
	u64 nr_buses;

	/* Vendor glue drivers may implement their own ECAM mechanism */
	if (pp->native_ecam)
		return false;

	/*
	 * PCIe spec r6.0, sec 7.2.2 mandates the base address used for ECAM to
	 * be aligned on a 2^(n+20) byte boundary, where n is the number of bits
	 * used for representing 'bus' in BDF. Since the DWC cores always use 8
	 * bits for representing 'bus', the base address has to be aligned to
	 * 2^28 byte boundary, which is 256 MiB.
	 */
	if (!IS_256MB_ALIGNED(config_res->start))
		return false;

	bus_range = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS)->res;
	if (!bus_range)
		return false;

	nr_buses = resource_size(config_res) >> PCIE_ECAM_BUS_SHIFT;

	return nr_buses >= resource_size(bus_range);
}

static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp)
{
	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
@@ -422,10 +514,6 @@ static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp)
	struct resource *res;
	int ret;

	ret = dw_pcie_get_resources(pci);
	if (ret)
		return ret;

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");
	if (!res) {
		dev_err(dev, "Missing \"config\" reg space\n");
@@ -435,10 +523,33 @@ static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp)
	pp->cfg0_size = resource_size(res);
	pp->cfg0_base = res->start;

	pp->ecam_enabled = dw_pcie_ecam_enabled(pp, res);
	if (pp->ecam_enabled) {
		ret = dw_pcie_create_ecam_window(pp, res);
		if (ret)
			return ret;

		pp->bridge->ops = (struct pci_ops *)&pci_generic_ecam_ops.pci_ops;
		pp->bridge->sysdata = pp->cfg;
		pp->cfg->priv = pp;
	} else {
		pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, res);
		if (IS_ERR(pp->va_cfg0_base))
			return PTR_ERR(pp->va_cfg0_base);

		/* Set default bus ops */
		pp->bridge->ops = &dw_pcie_ops;
		pp->bridge->child_ops = &dw_child_pcie_ops;
		pp->bridge->sysdata = pp;
	}

	ret = dw_pcie_get_resources(pci);
	if (ret) {
		if (pp->cfg)
			pci_ecam_free(pp->cfg);
		return ret;
	}

	/* Get the I/O range from DT */
	win = resource_list_first_type(&pp->bridge->windows, IORESOURCE_IO);
	if (win) {
@@ -476,14 +587,10 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
	if (ret)
		return ret;

	/* Set default bus ops */
	bridge->ops = &dw_pcie_ops;
	bridge->child_ops = &dw_child_pcie_ops;

	if (pp->ops->init) {
		ret = pp->ops->init(pp);
		if (ret)
			return ret;
			goto err_free_ecam;
	}

	if (pci_msi_enabled()) {
@@ -525,6 +632,14 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
	if (ret)
		goto err_free_msi;

	if (pp->ecam_enabled) {
		ret = dw_pcie_config_ecam_iatu(pp);
		if (ret) {
			dev_err(dev, "Failed to configure iATU in ECAM mode\n");
			goto err_free_msi;
		}
	}

	/*
	 * Allocate the resource for MSG TLP before programming the iATU
	 * outbound window in dw_pcie_setup_rc(). Since the allocation depends
@@ -560,8 +675,6 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
		/* Ignore errors, the link may come up later */
		dw_pcie_wait_for_link(pci);

	bridge->sysdata = pp;

	ret = pci_host_probe(bridge);
	if (ret)
		goto err_stop_link;
@@ -587,6 +700,10 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
	if (pp->ops->deinit)
		pp->ops->deinit(pp);

err_free_ecam:
	if (pp->cfg)
		pci_ecam_free(pp->cfg);

	return ret;
}
EXPORT_SYMBOL_GPL(dw_pcie_host_init);
@@ -609,6 +726,9 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp)

	if (pp->ops->deinit)
		pp->ops->deinit(pp);

	if (pp->cfg)
		pci_ecam_free(pp->cfg);
}
EXPORT_SYMBOL_GPL(dw_pcie_host_deinit);

+9 −1
Original line number Diff line number Diff line
@@ -167,6 +167,14 @@ int dw_pcie_get_resources(struct dw_pcie *pci)
		}
	}

	/* ELBI is an optional resource */
	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
	if (res) {
		pci->elbi_base = devm_ioremap_resource(pci->dev, res);
		if (IS_ERR(pci->elbi_base))
			return PTR_ERR(pci->elbi_base);
	}

	/* 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);
@@ -500,7 +508,7 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
		val = dw_pcie_enable_ecrc(val);
	dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val);

	val = PCIE_ATU_ENABLE;
	val = PCIE_ATU_ENABLE | atu->ctrl2;
	if (atu->type == PCIE_ATU_TYPE_MSG) {
		/* The data-less messages only for now */
		val |= PCIE_ATU_INHIBIT_PAYLOAD | atu->code;
Loading