Commit 349b434b authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/controller/dwc'

- Fix potential string truncation in dw_pcie_edma_irq_verify() (Niklas
  Cassel)

- Don't wait for link up in DWC core if driver can detect Link Up event
  (Krishna chaitanya chundru)

- If qcom 'global' IRQ is supported for detection of Link Up events, tell
  DWC core not to wait for link up (Krishna chaitanya chundru)

- Update ICC and OPP votes after Link Up events (Krishna chaitanya chundru)

- Use dw-rockchip dll_link_up IRQ to detect Link Up and enumerate devices
  so users don't have to manually rescan (Niklas Cassel)

- In dw-rockchip, the 'sys' interrupt is required and detects Link Up
  events, so tell DWC core not to wait for link up (Niklas Cassel)

- Always stop link in dw_pcie_suspend_noirq(), which is required at least
  for i.MX8QM to re-establish link on resume (Richard Zhu)

- Drop racy and unnecessary LTSSM state check before sending PME_TURN_OFF
  message in dw_pcie_suspend_noirq() (Richard Zhu)

- Add stubs for dw_pcie_suspend_noirq() dw_pcie_resume_noirq() when
  CONFIG_PCIE_DW_HOST is not defined so drivers don't need #ifdefs (Bjorn
  Helgaas)

- Use DWC core suspend/resume functions for imx6 (Frank Li)

- Add imx6 suspend/resume support for i.MX8MQ, i.MX8Q, and i.MX95 (Richard
  Zhu)

- Add struct of_pci_range.parent_bus_addr for devices that need their
  immediate parent bus address, not the CPU address, e.g., to program an
  internal Address Translation Unit (iATU) (Frank Li)

* pci/controller/dwc:
  PCI: dwc: Simplify config resource lookup
  of: address: Add parent_bus_addr to struct of_pci_range
  PCI: imx6: Add i.MX8MQ, i.MX8Q and i.MX95 PM support
  PCI: imx6: Use DWC common suspend resume method
  PCI: dwc: Add dw_pcie_suspend_noirq(), dw_pcie_resume_noirq() stubs for !CONFIG_PCIE_DW_HOST
  PCI: dwc: Remove LTSSM state test in dw_pcie_suspend_noirq()
  PCI: dwc: Always stop link in the dw_pcie_suspend_noirq
  PCI: dw-rockchip: Don't wait for link since we can detect Link Up
  PCI: dw-rockchip: Enumerate endpoints based on dll_link_up IRQ
  PCI: qcom: Update ICC and OPP values after Link Up event
  PCI: qcom: Don't wait for link if we can detect Link Up
  PCI: dwc: Don't wait for link up if driver can detect Link Up event
  PCI: dwc: Fix potential truncation in dw_pcie_edma_irq_verify()

# Conflicts:
#	drivers/pci/controller/dwc/pci-imx6.c
parents 8ee6c616 1108d677
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -815,6 +815,8 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
	else
		range->cpu_addr = of_translate_address(parser->node,
				parser->range + na);

	range->parent_bus_addr = of_read_number(parser->range + na, parser->pna);
	range->size = of_read_number(parser->range + parser->pna + na, ns);

	parser->range += np;
+41 −64
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>

#include "../../pci.h"
#include "pcie-designware.h"

#define IMX8MQ_GPR_PCIE_REF_USE_PAD		BIT(9)
@@ -128,19 +129,18 @@ struct imx_pcie_drvdata {
	int (*init_phy)(struct imx_pcie *pcie);
	int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable);
	int (*core_reset)(struct imx_pcie *pcie, bool assert);
	const struct dw_pcie_host_ops *ops;
};

struct imx_pcie {
	struct dw_pcie		*pci;
	struct gpio_desc	*reset_gpiod;
	bool			link_is_up;
	struct clk_bulk_data	clks[IMX_PCIE_MAX_CLKS];
	struct regmap		*iomuxc_gpr;
	u16			msi_ctrl;
	u32			controller_id;
	struct reset_control	*pciephy_reset;
	struct reset_control	*apps_reset;
	struct reset_control	*turnoff_reset;
	u32			tx_deemph_gen1;
	u32			tx_deemph_gen2_3p5db;
	u32			tx_deemph_gen2_6db;
@@ -928,13 +928,11 @@ static int imx_pcie_start_link(struct dw_pcie *pci)
		dev_info(dev, "Link: Only Gen1 is enabled\n");
	}

	imx_pcie->link_is_up = true;
	tmp = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA);
	dev_info(dev, "Link up, Gen%i\n", tmp & PCI_EXP_LNKSTA_CLS);
	return 0;

err_reset_phy:
	imx_pcie->link_is_up = false;
	dev_dbg(dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n",
		dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG0),
		dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG1));
@@ -1236,9 +1234,31 @@ static u64 imx_pcie_cpu_addr_fixup(struct dw_pcie *pcie, u64 cpu_addr)
	return cpu_addr - entry->offset;
}

/*
 * In old DWC implementations, PCIE_ATU_INHIBIT_PAYLOAD in iATU Ctrl2
 * register is reserved, so the generic DWC implementation of sending the
 * PME_Turn_Off message using a dummy MMIO write cannot be used.
 */
static void imx_pcie_pme_turn_off(struct dw_pcie_rp *pp)
{
	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
	struct imx_pcie *imx_pcie = to_imx_pcie(pci);

	regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6SX_GPR12_PCIE_PM_TURN_OFF);
	regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6SX_GPR12_PCIE_PM_TURN_OFF);

	usleep_range(PCIE_PME_TO_L2_TIMEOUT_US/10, PCIE_PME_TO_L2_TIMEOUT_US);
}

static const struct dw_pcie_host_ops imx_pcie_host_ops = {
	.init = imx_pcie_host_init,
	.deinit = imx_pcie_host_exit,
	.pme_turn_off = imx_pcie_pme_turn_off,
};

static const struct dw_pcie_host_ops imx_pcie_host_dw_pme_ops = {
	.init = imx_pcie_host_init,
	.deinit = imx_pcie_host_exit,
};

static const struct dw_pcie_ops dw_pcie_ops = {
@@ -1379,43 +1399,6 @@ static int imx_add_pcie_ep(struct imx_pcie *imx_pcie,
	return 0;
}

static void imx_pcie_pm_turnoff(struct imx_pcie *imx_pcie)
{
	struct device *dev = imx_pcie->pci->dev;

	/* Some variants have a turnoff reset in DT */
	if (imx_pcie->turnoff_reset) {
		reset_control_assert(imx_pcie->turnoff_reset);
		reset_control_deassert(imx_pcie->turnoff_reset);
		goto pm_turnoff_sleep;
	}

	/* Others poke directly at IOMUXC registers */
	switch (imx_pcie->drvdata->variant) {
	case IMX6SX:
	case IMX6QP:
		regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
				IMX6SX_GPR12_PCIE_PM_TURN_OFF,
				IMX6SX_GPR12_PCIE_PM_TURN_OFF);
		regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
				IMX6SX_GPR12_PCIE_PM_TURN_OFF, 0);
		break;
	default:
		dev_err(dev, "PME_Turn_Off not implemented\n");
		return;
	}

	/*
	 * Components with an upstream port must respond to
	 * PME_Turn_Off with PME_TO_Ack but we can't check.
	 *
	 * The standard recommends a 1-10ms timeout after which to
	 * proceed anyway as if acks were received.
	 */
pm_turnoff_sleep:
	usleep_range(1000, 10000);
}

static void imx_pcie_msi_save_restore(struct imx_pcie *imx_pcie, bool save)
{
	u8 offset;
@@ -1439,7 +1422,6 @@ static void imx_pcie_msi_save_restore(struct imx_pcie *imx_pcie, bool save)
static int imx_pcie_suspend_noirq(struct device *dev)
{
	struct imx_pcie *imx_pcie = dev_get_drvdata(dev);
	struct dw_pcie_rp *pp = &imx_pcie->pci->pp;

	if (!(imx_pcie->drvdata->flags & IMX_PCIE_FLAG_SUPPORTS_SUSPEND))
		return 0;
@@ -1454,9 +1436,7 @@ static int imx_pcie_suspend_noirq(struct device *dev)
		imx_pcie_assert_core_reset(imx_pcie);
		imx_pcie->drvdata->enable_ref_clk(imx_pcie, false);
	} else {
		imx_pcie_pm_turnoff(imx_pcie);
		imx_pcie_stop_link(imx_pcie->pci);
		imx_pcie_host_exit(pp);
		return dw_pcie_suspend_noirq(imx_pcie->pci);
	}

	return 0;
@@ -1466,7 +1446,6 @@ static int imx_pcie_resume_noirq(struct device *dev)
{
	int ret;
	struct imx_pcie *imx_pcie = dev_get_drvdata(dev);
	struct dw_pcie_rp *pp = &imx_pcie->pci->pp;

	if (!(imx_pcie->drvdata->flags & IMX_PCIE_FLAG_SUPPORTS_SUSPEND))
		return 0;
@@ -1486,17 +1465,12 @@ static int imx_pcie_resume_noirq(struct device *dev)
		ret = dw_pcie_setup_rc(&imx_pcie->pci->pp);
		if (ret)
			return ret;
		imx_pcie_msi_save_restore(imx_pcie, false);
	} else {
		ret = imx_pcie_host_init(pp);
		ret = dw_pcie_resume_noirq(imx_pcie->pci);
		if (ret)
			return ret;
		imx_pcie_msi_save_restore(imx_pcie, false);
		dw_pcie_setup_rc(pp);

		if (imx_pcie->link_is_up)
			imx_pcie_start_link(imx_pcie->pci);
	}
	imx_pcie_msi_save_restore(imx_pcie, false);

	return 0;
}
@@ -1528,13 +1502,17 @@ static int imx_pcie_probe(struct platform_device *pdev)

	pci->dev = dev;
	pci->ops = &dw_pcie_ops;
	pci->pp.ops = &imx_pcie_host_ops;

	imx_pcie->pci = pci;
	imx_pcie->drvdata = of_device_get_match_data(dev);

	mutex_init(&imx_pcie->lock);

	if (imx_pcie->drvdata->ops)
		pci->pp.ops = imx_pcie->drvdata->ops;
	else
		pci->pp.ops = &imx_pcie_host_dw_pme_ops;

	/* Find the PHY if one is defined, only imx7d uses it */
	np = of_parse_phandle(node, "fsl,imx7d-pcie-phy", 0);
	if (np) {
@@ -1604,13 +1582,6 @@ static int imx_pcie_probe(struct platform_device *pdev)
		break;
	}

	/* Grab turnoff reset */
	imx_pcie->turnoff_reset = devm_reset_control_get_optional_exclusive(dev, "turnoff");
	if (IS_ERR(imx_pcie->turnoff_reset)) {
		dev_err(dev, "Failed to get TURNOFF reset control\n");
		return PTR_ERR(imx_pcie->turnoff_reset);
	}

	if (imx_pcie->drvdata->gpr) {
	/* Grab GPR config register range */
		imx_pcie->iomuxc_gpr =
@@ -1689,6 +1660,7 @@ static int imx_pcie_probe(struct platform_device *pdev)
		if (ret < 0)
			return ret;
	} else {
		pci->pp.use_atu_msg = true;
		ret = dw_pcie_host_init(&pci->pp);
		if (ret < 0)
			return ret;
@@ -1753,6 +1725,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
		.init_phy = imx6sx_pcie_init_phy,
		.enable_ref_clk = imx6sx_pcie_enable_ref_clk,
		.core_reset = imx6sx_pcie_core_reset,
		.ops = &imx_pcie_host_ops,
	},
	[IMX6QP] = {
		.variant = IMX6QP,
@@ -1770,6 +1743,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
		.init_phy = imx_pcie_init_phy,
		.enable_ref_clk = imx6q_pcie_enable_ref_clk,
		.core_reset = imx6qp_pcie_core_reset,
		.ops = &imx_pcie_host_ops,
	},
	[IMX7D] = {
		.variant = IMX7D,
@@ -1788,7 +1762,8 @@ static const struct imx_pcie_drvdata drvdata[] = {
	[IMX8MQ] = {
		.variant = IMX8MQ,
		.flags = IMX_PCIE_FLAG_HAS_APP_RESET |
			 IMX_PCIE_FLAG_HAS_PHY_RESET,
			 IMX_PCIE_FLAG_HAS_PHY_RESET |
			 IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
		.gpr = "fsl,imx8mq-iomuxc-gpr",
		.clk_names = imx8mq_clks,
		.clks_cnt = ARRAY_SIZE(imx8mq_clks),
@@ -1826,14 +1801,16 @@ static const struct imx_pcie_drvdata drvdata[] = {
	[IMX8Q] = {
		.variant = IMX8Q,
		.flags = IMX_PCIE_FLAG_HAS_PHYDRV |
			 IMX_PCIE_FLAG_CPU_ADDR_FIXUP,
			 IMX_PCIE_FLAG_CPU_ADDR_FIXUP |
			 IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
		.clk_names = imx8q_clks,
		.clks_cnt = ARRAY_SIZE(imx8q_clks),
	},
	[IMX95] = {
		.variant = IMX95,
		.flags = IMX_PCIE_FLAG_HAS_SERDES |
			 IMX_PCIE_FLAG_HAS_LUT,
			 IMX_PCIE_FLAG_HAS_LUT |
			 IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
		.clk_names = imx8mq_clks,
		.clks_cnt = ARRAY_SIZE(imx8mq_clks),
		.ltssm_off = IMX95_PE0_GEN_CTRL_3,
+35 −21
Original line number Diff line number Diff line
@@ -436,17 +436,17 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
		return ret;

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");
	if (res) {
	if (!res) {
		dev_err(dev, "Missing \"config\" reg space\n");
		return -ENODEV;
	}

	pp->cfg0_size = resource_size(res);
	pp->cfg0_base = res->start;

	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);
	} else {
		dev_err(dev, "Missing *config* reg space\n");
		return -ENODEV;
	}

	bridge = devm_pci_alloc_host_bridge(dev, 0);
	if (!bridge)
@@ -530,6 +530,12 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
			goto err_remove_edma;
	}

	/*
	 * Note: Skip the link up delay only when a Link Up IRQ is present.
	 * If there is no Link Up IRQ, we should not bypass the delay
	 * because that would require users to manually rescan for devices.
	 */
	if (!pp->use_linkup_irq)
		/* Ignore errors, the link may come up later */
		dw_pcie_wait_for_link(pci);

@@ -918,7 +924,7 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci)
{
	u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
	u32 val;
	int ret = 0;
	int ret;

	/*
	 * If L1SS is supported, then do not put the link into L2 as some
@@ -927,25 +933,33 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci)
	if (dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKCTL) & PCI_EXP_LNKCTL_ASPM_L1)
		return 0;

	if (dw_pcie_get_ltssm(pci) <= DW_PCIE_LTSSM_DETECT_ACT)
		return 0;

	if (pci->pp.ops->pme_turn_off)
	if (pci->pp.ops->pme_turn_off) {
		pci->pp.ops->pme_turn_off(&pci->pp);
	else
	} else {
		ret = dw_pcie_pme_turn_off(pci);

		if (ret)
			return ret;
	}

	ret = read_poll_timeout(dw_pcie_get_ltssm, val, val == DW_PCIE_LTSSM_L2_IDLE,
	ret = read_poll_timeout(dw_pcie_get_ltssm, val,
				val == DW_PCIE_LTSSM_L2_IDLE ||
				val <= DW_PCIE_LTSSM_DETECT_WAIT,
				PCIE_PME_TO_L2_TIMEOUT_US/10,
				PCIE_PME_TO_L2_TIMEOUT_US, false, pci);
	if (ret) {
		/* Only log message when LTSSM isn't in DETECT or POLL */
		dev_err(pci->dev, "Timeout waiting for L2 entry! LTSSM: 0x%x\n", val);
		return ret;
	}

	/*
	 * Per PCIe r6.0, sec 5.3.3.2.1, software should wait at least
	 * 100ns after L2/L3 Ready before turning off refclock and
	 * main power. This is harmless when no endpoint is connected.
	 */
	udelay(1);

	dw_pcie_stop_link(pci);
	if (pci->pp.ops->deinit)
		pci->pp.ops->deinit(&pci->pp);

+1 −1
Original line number Diff line number Diff line
@@ -971,7 +971,7 @@ static int dw_pcie_edma_irq_verify(struct dw_pcie *pci)
{
	struct platform_device *pdev = to_platform_device(pci->dev);
	u16 ch_cnt = pci->edma.ll_wr_cnt + pci->edma.ll_rd_cnt;
	char name[6];
	char name[15];
	int ret;

	if (pci->edma.nr_irqs == 1)
+14 −3
Original line number Diff line number Diff line
@@ -330,6 +330,7 @@ enum dw_pcie_ltssm {
	/* Need to align with PCIE_PORT_DEBUG0 bits 0:5 */
	DW_PCIE_LTSSM_DETECT_QUIET = 0x0,
	DW_PCIE_LTSSM_DETECT_ACT = 0x1,
	DW_PCIE_LTSSM_DETECT_WAIT = 0x6,
	DW_PCIE_LTSSM_L0 = 0x11,
	DW_PCIE_LTSSM_L2_IDLE = 0x15,

@@ -379,6 +380,7 @@ struct dw_pcie_rp {
	bool			use_atu_msg;
	int			msg_atu_index;
	struct resource		*msg_res;
	bool			use_linkup_irq;
};

struct dw_pcie_ep_ops {
@@ -498,9 +500,6 @@ void dw_pcie_iatu_detect(struct dw_pcie *pci);
int dw_pcie_edma_detect(struct dw_pcie *pci);
void dw_pcie_edma_remove(struct dw_pcie *pci);

int dw_pcie_suspend_noirq(struct dw_pcie *pci);
int dw_pcie_resume_noirq(struct dw_pcie *pci);

static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val)
{
	dw_pcie_write_dbi(pci, reg, 0x4, val);
@@ -678,6 +677,8 @@ static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci)
}

#ifdef CONFIG_PCIE_DW_HOST
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);
int dw_pcie_setup_rc(struct dw_pcie_rp *pp);
int dw_pcie_host_init(struct dw_pcie_rp *pp);
@@ -686,6 +687,16 @@ int dw_pcie_allocate_domains(struct dw_pcie_rp *pp);
void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus, unsigned int devfn,
				       int where);
#else
static inline int dw_pcie_suspend_noirq(struct dw_pcie *pci)
{
	return 0;
}

static inline int dw_pcie_resume_noirq(struct dw_pcie *pci)
{
	return 0;
}

static inline irqreturn_t dw_handle_msi_irq(struct dw_pcie_rp *pp)
{
	return IRQ_NONE;
Loading