Commit c2863b21 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'remotes/lorenzo/pci/rcar'

- Fix runtime PM imbalance in rcar_pcie_ep_probe() (Dinghao Liu)

* remotes/lorenzo/pci/rcar:
  PCI: rcar: Add L1 link state fix into data abort hook
  PCI: rcar: Fix runtime PM imbalance in rcar_pcie_ep_probe()
parents c501cf9c a115b1bd
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -492,9 +492,9 @@ static int rcar_pcie_ep_probe(struct platform_device *pdev)
	pcie->dev = dev;

	pm_runtime_enable(dev);
	err = pm_runtime_get_sync(dev);
	err = pm_runtime_resume_and_get(dev);
	if (err < 0) {
		dev_err(dev, "pm_runtime_get_sync failed\n");
		dev_err(dev, "pm_runtime_resume_and_get failed\n");
		goto err_pm_disable;
	}

+86 −0
Original line number Diff line number Diff line
@@ -13,12 +13,14 @@

#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/iopoll.h>
#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -41,6 +43,21 @@ struct rcar_msi {
	int irq2;
};

#ifdef CONFIG_ARM
/*
 * Here we keep a static copy of the remapped PCIe controller address.
 * This is only used on aarch32 systems, all of which have one single
 * PCIe controller, to provide quick access to the PCIe controller in
 * the L1 link state fixup function, called from the ARM fault handler.
 */
static void __iomem *pcie_base;
/*
 * Static copy of bus clock pointer, so we can check whether the clock
 * is enabled or not.
 */
static struct clk *pcie_bus_clk;
#endif

/* Structure representing the PCIe interface */
struct rcar_pcie_host {
	struct rcar_pcie	pcie;
@@ -774,6 +791,12 @@ static int rcar_pcie_get_resources(struct rcar_pcie_host *host)
	}
	host->msi.irq2 = i;

#ifdef CONFIG_ARM
	/* Cache static copy for L1 link state fixup hook on aarch32 */
	pcie_base = pcie->base;
	pcie_bus_clk = host->bus_clk;
#endif

	return 0;

err_irq2:
@@ -1029,4 +1052,67 @@ static struct platform_driver rcar_pcie_driver = {
	},
	.probe = rcar_pcie_probe,
};

#ifdef CONFIG_ARM
static DEFINE_SPINLOCK(pmsr_lock);
static int rcar_pcie_aarch32_abort_handler(unsigned long addr,
		unsigned int fsr, struct pt_regs *regs)
{
	unsigned long flags;
	u32 pmsr, val;
	int ret = 0;

	spin_lock_irqsave(&pmsr_lock, flags);

	if (!pcie_base || !__clk_is_enabled(pcie_bus_clk)) {
		ret = 1;
		goto unlock_exit;
	}

	pmsr = readl(pcie_base + PMSR);

	/*
	 * Test if the PCIe controller received PM_ENTER_L1 DLLP and
	 * the PCIe controller is not in L1 link state. If true, apply
	 * fix, which will put the controller into L1 link state, from
	 * which it can return to L0s/L0 on its own.
	 */
	if ((pmsr & PMEL1RX) && ((pmsr & PMSTATE) != PMSTATE_L1)) {
		writel(L1IATN, pcie_base + PMCTLR);
		ret = readl_poll_timeout_atomic(pcie_base + PMSR, val,
						val & L1FAEG, 10, 1000);
		WARN(ret, "Timeout waiting for L1 link state, ret=%d\n", ret);
		writel(L1FAEG | PMEL1RX, pcie_base + PMSR);
	}

unlock_exit:
	spin_unlock_irqrestore(&pmsr_lock, flags);
	return ret;
}

static const struct of_device_id rcar_pcie_abort_handler_of_match[] __initconst = {
	{ .compatible = "renesas,pcie-r8a7779" },
	{ .compatible = "renesas,pcie-r8a7790" },
	{ .compatible = "renesas,pcie-r8a7791" },
	{ .compatible = "renesas,pcie-rcar-gen2" },
	{},
};

static int __init rcar_pcie_init(void)
{
	if (of_find_matching_node(NULL, rcar_pcie_abort_handler_of_match)) {
#ifdef CONFIG_ARM_LPAE
		hook_fault_code(17, rcar_pcie_aarch32_abort_handler, SIGBUS, 0,
				"asynchronous external abort");
#else
		hook_fault_code(22, rcar_pcie_aarch32_abort_handler, SIGBUS, 0,
				"imprecise external abort");
#endif
	}

	return platform_driver_register(&rcar_pcie_driver);
}
device_initcall(rcar_pcie_init);
#else
builtin_platform_driver(rcar_pcie_driver);
#endif
+7 −0
Original line number Diff line number Diff line
@@ -85,6 +85,13 @@
#define  LTSMDIS		BIT(31)
#define  MACCTLR_INIT_VAL	(LTSMDIS | MACCTLR_NFTS_MASK)
#define PMSR			0x01105c
#define  L1FAEG			BIT(31)
#define  PMEL1RX		BIT(23)
#define  PMSTATE		GENMASK(18, 16)
#define  PMSTATE_L1		(3 << 16)
#define PMCTLR			0x011060
#define  L1IATN			BIT(31)

#define MACS2R			0x011078
#define MACCGSPSETR		0x011084
#define  SPCNGRSN		BIT(31)