diff options
| -rw-r--r-- | drivers/pci/controller/pcie-rcar-host.c | 130 | 
1 files changed, 96 insertions, 34 deletions
| diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index dfca59c4ae34..997c4df6a1e7 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -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 = { |