mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 09:19:51 +00:00
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()
This commit is contained in:
commit
4b0f6ecaba
1 changed files with 96 additions and 34 deletions
|
@ -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 = {
|
||||
|
|
Loading…
Reference in a new issue