Skip to content

Commit 4b0f6ec

Browse files
committed
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()
2 parents 0c634fc + 6e36203 commit 4b0f6ec

1 file changed

Lines changed: 96 additions & 34 deletions

File tree

drivers/pci/controller/pcie-rcar-host.c

Lines changed: 96 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,42 @@ struct rcar_pcie_host {
6565
int (*phy_init_fn)(struct rcar_pcie_host *host);
6666
};
6767

68+
static DEFINE_SPINLOCK(pmsr_lock);
69+
70+
static int rcar_pcie_wakeup(struct device *pcie_dev, void __iomem *pcie_base)
71+
{
72+
unsigned long flags;
73+
u32 pmsr, val;
74+
int ret = 0;
75+
76+
spin_lock_irqsave(&pmsr_lock, flags);
77+
78+
if (!pcie_base || pm_runtime_suspended(pcie_dev)) {
79+
ret = -EINVAL;
80+
goto unlock_exit;
81+
}
82+
83+
pmsr = readl(pcie_base + PMSR);
84+
85+
/*
86+
* Test if the PCIe controller received PM_ENTER_L1 DLLP and
87+
* the PCIe controller is not in L1 link state. If true, apply
88+
* fix, which will put the controller into L1 link state, from
89+
* which it can return to L0s/L0 on its own.
90+
*/
91+
if ((pmsr & PMEL1RX) && ((pmsr & PMSTATE) != PMSTATE_L1)) {
92+
writel(L1IATN, pcie_base + PMCTLR);
93+
ret = readl_poll_timeout_atomic(pcie_base + PMSR, val,
94+
val & L1FAEG, 10, 1000);
95+
WARN(ret, "Timeout waiting for L1 link state, ret=%d\n", ret);
96+
writel(L1FAEG | PMEL1RX, pcie_base + PMSR);
97+
}
98+
99+
unlock_exit:
100+
spin_unlock_irqrestore(&pmsr_lock, flags);
101+
return ret;
102+
}
103+
68104
static struct rcar_pcie_host *msi_to_host(struct rcar_msi *msi)
69105
{
70106
return container_of(msi, struct rcar_pcie_host, msi);
@@ -78,13 +114,69 @@ static u32 rcar_read_conf(struct rcar_pcie *pcie, int where)
78114
return val >> shift;
79115
}
80116

117+
#ifdef CONFIG_ARM
118+
#define __rcar_pci_rw_reg_workaround(instr) \
119+
" .arch armv7-a\n" \
120+
"1: " instr " %1, [%2]\n" \
121+
"2: isb\n" \
122+
"3: .pushsection .text.fixup,\"ax\"\n" \
123+
" .align 2\n" \
124+
"4: mov %0, #" __stringify(PCIBIOS_SET_FAILED) "\n" \
125+
" b 3b\n" \
126+
" .popsection\n" \
127+
" .pushsection __ex_table,\"a\"\n" \
128+
" .align 3\n" \
129+
" .long 1b, 4b\n" \
130+
" .long 2b, 4b\n" \
131+
" .popsection\n"
132+
#endif
133+
134+
static int rcar_pci_write_reg_workaround(struct rcar_pcie *pcie, u32 val,
135+
unsigned int reg)
136+
{
137+
int error = PCIBIOS_SUCCESSFUL;
138+
#ifdef CONFIG_ARM
139+
asm volatile(
140+
__rcar_pci_rw_reg_workaround("str")
141+
: "+r"(error):"r"(val), "r"(pcie->base + reg) : "memory");
142+
#else
143+
rcar_pci_write_reg(pcie, val, reg);
144+
#endif
145+
return error;
146+
}
147+
148+
static int rcar_pci_read_reg_workaround(struct rcar_pcie *pcie, u32 *val,
149+
unsigned int reg)
150+
{
151+
int error = PCIBIOS_SUCCESSFUL;
152+
#ifdef CONFIG_ARM
153+
asm volatile(
154+
__rcar_pci_rw_reg_workaround("ldr")
155+
: "+r"(error), "=r"(*val) : "r"(pcie->base + reg) : "memory");
156+
157+
if (error != PCIBIOS_SUCCESSFUL)
158+
PCI_SET_ERROR_RESPONSE(val);
159+
#else
160+
*val = rcar_pci_read_reg(pcie, reg);
161+
#endif
162+
return error;
163+
}
164+
81165
/* Serialization is provided by 'pci_lock' in drivers/pci/access.c */
82166
static int rcar_pcie_config_access(struct rcar_pcie_host *host,
83167
unsigned char access_type, struct pci_bus *bus,
84168
unsigned int devfn, int where, u32 *data)
85169
{
86170
struct rcar_pcie *pcie = &host->pcie;
87171
unsigned int dev, func, reg, index;
172+
int ret;
173+
174+
/* Wake the bus up in case it is in L1 state. */
175+
ret = rcar_pcie_wakeup(pcie->dev, pcie->base);
176+
if (ret) {
177+
PCI_SET_ERROR_RESPONSE(data);
178+
return PCIBIOS_SET_FAILED;
179+
}
88180

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

143235
if (access_type == RCAR_PCI_ACCESS_READ)
144-
*data = rcar_pci_read_reg(pcie, PCIECDR);
236+
ret = rcar_pci_read_reg_workaround(pcie, data, PCIECDR);
145237
else
146-
rcar_pci_write_reg(pcie, *data, PCIECDR);
238+
ret = rcar_pci_write_reg_workaround(pcie, *data, PCIECDR);
147239

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

151-
return PCIBIOS_SUCCESSFUL;
243+
return ret;
152244
}
153245

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

10521144
#ifdef CONFIG_ARM
1053-
static DEFINE_SPINLOCK(pmsr_lock);
10541145
static int rcar_pcie_aarch32_abort_handler(unsigned long addr,
10551146
unsigned int fsr, struct pt_regs *regs)
10561147
{
1057-
unsigned long flags;
1058-
u32 pmsr, val;
1059-
int ret = 0;
1060-
1061-
spin_lock_irqsave(&pmsr_lock, flags);
1062-
1063-
if (!pcie_base || pm_runtime_suspended(pcie_dev)) {
1064-
ret = 1;
1065-
goto unlock_exit;
1066-
}
1067-
1068-
pmsr = readl(pcie_base + PMSR);
1069-
1070-
/*
1071-
* Test if the PCIe controller received PM_ENTER_L1 DLLP and
1072-
* the PCIe controller is not in L1 link state. If true, apply
1073-
* fix, which will put the controller into L1 link state, from
1074-
* which it can return to L0s/L0 on its own.
1075-
*/
1076-
if ((pmsr & PMEL1RX) && ((pmsr & PMSTATE) != PMSTATE_L1)) {
1077-
writel(L1IATN, pcie_base + PMCTLR);
1078-
ret = readl_poll_timeout_atomic(pcie_base + PMSR, val,
1079-
val & L1FAEG, 10, 1000);
1080-
WARN(ret, "Timeout waiting for L1 link state, ret=%d\n", ret);
1081-
writel(L1FAEG | PMEL1RX, pcie_base + PMSR);
1082-
}
1083-
1084-
unlock_exit:
1085-
spin_unlock_irqrestore(&pmsr_lock, flags);
1086-
return ret;
1148+
return !fixup_exception(regs);
10871149
}
10881150

10891151
static const struct of_device_id rcar_pcie_abort_handler_of_match[] __initconst = {

0 commit comments

Comments
 (0)