Commit 4b0f6eca authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'remotes/lorenzo/pci/rcar'

- Finish transition to L1 state in rcar_pcie_config_access() because R-Car
  can't do it on its own (Marek Vasut)

- Return PCI_ERROR_RESPONSE for reads that trigger PCIe errors (Marek
  Vasut)

* remotes/lorenzo/pci/rcar:
  PCI: rcar: Use PCI_SET_ERROR_RESPONSE after read which triggered an exception
  PCI: rcar: Finish transition to L1 state in rcar_pcie_config_access()
parents 0c634fcb 6e36203b
Loading
Loading
Loading
Loading
+96 −34
Original line number Diff line number Diff line
@@ -65,6 +65,42 @@ struct rcar_pcie_host {
	int			(*phy_init_fn)(struct rcar_pcie_host *host);
};

static DEFINE_SPINLOCK(pmsr_lock);

static int rcar_pcie_wakeup(struct device *pcie_dev, void __iomem *pcie_base)
{
	unsigned long flags;
	u32 pmsr, val;
	int ret = 0;

	spin_lock_irqsave(&pmsr_lock, flags);

	if (!pcie_base || pm_runtime_suspended(pcie_dev)) {
		ret = -EINVAL;
		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 struct rcar_pcie_host *msi_to_host(struct rcar_msi *msi)
{
	return container_of(msi, struct rcar_pcie_host, msi);
@@ -78,6 +114,54 @@ static u32 rcar_read_conf(struct rcar_pcie *pcie, int where)
	return val >> shift;
}

#ifdef CONFIG_ARM
#define __rcar_pci_rw_reg_workaround(instr)				\
		"	.arch armv7-a\n"				\
		"1:	" instr " %1, [%2]\n"				\
		"2:	isb\n"						\
		"3:	.pushsection .text.fixup,\"ax\"\n"		\
		"	.align	2\n"					\
		"4:	mov	%0, #" __stringify(PCIBIOS_SET_FAILED) "\n" \
		"	b	3b\n"					\
		"	.popsection\n"					\
		"	.pushsection __ex_table,\"a\"\n"		\
		"	.align	3\n"					\
		"	.long	1b, 4b\n"				\
		"	.long	2b, 4b\n"				\
		"	.popsection\n"
#endif

static int rcar_pci_write_reg_workaround(struct rcar_pcie *pcie, u32 val,
					 unsigned int reg)
{
	int error = PCIBIOS_SUCCESSFUL;
#ifdef CONFIG_ARM
	asm volatile(
		__rcar_pci_rw_reg_workaround("str")
	: "+r"(error):"r"(val), "r"(pcie->base + reg) : "memory");
#else
	rcar_pci_write_reg(pcie, val, reg);
#endif
	return error;
}

static int rcar_pci_read_reg_workaround(struct rcar_pcie *pcie, u32 *val,
					unsigned int reg)
{
	int error = PCIBIOS_SUCCESSFUL;
#ifdef CONFIG_ARM
	asm volatile(
		__rcar_pci_rw_reg_workaround("ldr")
	: "+r"(error), "=r"(*val) : "r"(pcie->base + reg) : "memory");

	if (error != PCIBIOS_SUCCESSFUL)
		PCI_SET_ERROR_RESPONSE(val);
#else
	*val = rcar_pci_read_reg(pcie, reg);
#endif
	return error;
}

/* Serialization is provided by 'pci_lock' in drivers/pci/access.c */
static int rcar_pcie_config_access(struct rcar_pcie_host *host,
		unsigned char access_type, struct pci_bus *bus,
@@ -85,6 +169,14 @@ static int rcar_pcie_config_access(struct rcar_pcie_host *host,
{
	struct rcar_pcie *pcie = &host->pcie;
	unsigned int dev, func, reg, index;
	int ret;

	/* Wake the bus up in case it is in L1 state. */
	ret = rcar_pcie_wakeup(pcie->dev, pcie->base);
	if (ret) {
		PCI_SET_ERROR_RESPONSE(data);
		return PCIBIOS_SET_FAILED;
	}

	dev = PCI_SLOT(devfn);
	func = PCI_FUNC(devfn);
@@ -141,14 +233,14 @@ static int rcar_pcie_config_access(struct rcar_pcie_host *host,
		return PCIBIOS_DEVICE_NOT_FOUND;

	if (access_type == RCAR_PCI_ACCESS_READ)
		*data = rcar_pci_read_reg(pcie, PCIECDR);
		ret = rcar_pci_read_reg_workaround(pcie, data, PCIECDR);
	else
		rcar_pci_write_reg(pcie, *data, PCIECDR);
		ret = rcar_pci_write_reg_workaround(pcie, *data, PCIECDR);

	/* Disable the configuration access */
	rcar_pci_write_reg(pcie, 0, PCIECCTLR);

	return PCIBIOS_SUCCESSFUL;
	return ret;
}

static int rcar_pcie_read_conf(struct pci_bus *bus, unsigned int devfn,
@@ -1050,40 +1142,10 @@ static struct platform_driver rcar_pcie_driver = {
};

#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 || pm_runtime_suspended(pcie_dev)) {
		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;
	return !fixup_exception(regs);
}

static const struct of_device_id rcar_pcie_abort_handler_of_match[] __initconst = {