PCI: rockchip: Use normal register bank for config accessors
Rockchip's RC has two banks of registers for the root port: a normal bank
that is strictly compatible with the PCIe spec, and a privileged bank that
can be used to change RO bits of root port registers.
When probing the RC driver, we use the privileged bank to do some basic
setup work as some RO bits are hw-inited to wrong value. But we didn't
change to the normal bank after probing the driver.
This leads to a serious problem when the PME code tries to clear the PME
status by writing PCI_EXP_RTSTA_PME to the register of PCI_EXP_RTSTA. Per
PCIe 3.0 spec, section 7.8.14, the PME status bit is RW1C. So the PME code
is doing the right thing to clear the PME status but we find the RC doesn't
clear it but actually setting it to one. So finally the system trap in
pcie_pme_work_fn() as PCI_EXP_RTSTA_PME is true now forever. This issue
can be reproduced by booting kernel with pci=nomsi.
Use the normal register bank for the PCI config accessors. The privileged
bank is used only internally by this driver.
Fixes: e77f847d
("PCI: rockchip: Add Rockchip PCIe controller support")
Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: stable@vger.kernel.org
Cc: Jeffy Chen <jeffy.chen@rock-chips.com>
Cc: Brian Norris <briannorris@chromium.org>
This commit is contained in:
parent
c2741cb6eb
commit
dc8cca5ef2
|
@ -139,6 +139,7 @@
|
|||
PCIE_CORE_INT_CT | PCIE_CORE_INT_UTC | \
|
||||
PCIE_CORE_INT_MMVC)
|
||||
|
||||
#define PCIE_RC_CONFIG_NORMAL_BASE 0x800000
|
||||
#define PCIE_RC_CONFIG_BASE 0xa00000
|
||||
#define PCIE_RC_CONFIG_RID_CCR (PCIE_RC_CONFIG_BASE + 0x08)
|
||||
#define PCIE_RC_CONFIG_SCC_SHIFT 16
|
||||
|
@ -301,7 +302,9 @@ static int rockchip_pcie_valid_device(struct rockchip_pcie *rockchip,
|
|||
static int rockchip_pcie_rd_own_conf(struct rockchip_pcie *rockchip,
|
||||
int where, int size, u32 *val)
|
||||
{
|
||||
void __iomem *addr = rockchip->apb_base + PCIE_RC_CONFIG_BASE + where;
|
||||
void __iomem *addr;
|
||||
|
||||
addr = rockchip->apb_base + PCIE_RC_CONFIG_NORMAL_BASE + where;
|
||||
|
||||
if (!IS_ALIGNED((uintptr_t)addr, size)) {
|
||||
*val = 0;
|
||||
|
@ -325,11 +328,13 @@ static int rockchip_pcie_wr_own_conf(struct rockchip_pcie *rockchip,
|
|||
int where, int size, u32 val)
|
||||
{
|
||||
u32 mask, tmp, offset;
|
||||
void __iomem *addr;
|
||||
|
||||
offset = where & ~0x3;
|
||||
addr = rockchip->apb_base + PCIE_RC_CONFIG_NORMAL_BASE + offset;
|
||||
|
||||
if (size == 4) {
|
||||
writel(val, rockchip->apb_base + PCIE_RC_CONFIG_BASE + offset);
|
||||
writel(val, addr);
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
|
@ -340,9 +345,9 @@ static int rockchip_pcie_wr_own_conf(struct rockchip_pcie *rockchip,
|
|||
* corrupt RW1C bits in adjacent registers. But the hardware
|
||||
* doesn't support smaller writes.
|
||||
*/
|
||||
tmp = readl(rockchip->apb_base + PCIE_RC_CONFIG_BASE + offset) & mask;
|
||||
tmp = readl(addr) & mask;
|
||||
tmp |= val << ((where & 0x3) * 8);
|
||||
writel(tmp, rockchip->apb_base + PCIE_RC_CONFIG_BASE + offset);
|
||||
writel(tmp, addr);
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue