mirror of
https://github.com/Fishwaldo/build.git
synced 2025-03-29 18:21:27 +00:00
129 lines
3.9 KiB
Diff
129 lines
3.9 KiB
Diff
From: Russell King <rmk+kernel@arm.linux.org.uk>
|
|
Subject: [PATCH 34/84] pci: mvebu: generate proper configuration access cycles
|
|
MIME-Version: 1.0
|
|
Content-Disposition: inline
|
|
Content-Transfer-Encoding: 8bit
|
|
Content-Type: text/plain; charset="utf-8"
|
|
|
|
The idea that you can arbitarily read 32-bits from PCI configuration
|
|
space, modify a sub-field (like the command register) and write it
|
|
back without consequence is deeply flawed.
|
|
|
|
Status registers (such as the status register, PCIe device status
|
|
register, etc) contain status bits which are read, write-one-to-clear.
|
|
|
|
What this means is that reading 32-bits from the command register,
|
|
modifying the command register, and then writing it back has the effect
|
|
of clearing any status bits that were indicating at that time. Same for
|
|
the PCIe device control register clearing bits in the PCIe device status
|
|
register.
|
|
|
|
Since the Armada chips support byte, 16-bit and 32-bit accesses to the
|
|
registers (unless otherwise stated) and the PCI configuration data
|
|
register does not specify otherwise, it seems logical that the chip can
|
|
indeed generate the proper configuration access cycles down to byte
|
|
level.
|
|
|
|
Testing with an ASM1062 PCIe to SATA mini-PCIe card on Armada 388.
|
|
PCIe capability at 0x80, DevCtl at 0x88, DevSta at 0x8a.
|
|
|
|
Before:
|
|
/# setpci -s 1:0.0 0x88.l - DevSta: CorrErr+
|
|
00012810
|
|
/# setpci -s 1:0.0 0x88.w=0x2810 - Write DevCtl only
|
|
/# setpci -s 1:0.0 0x88.l - CorrErr cleared - FAIL
|
|
00002810
|
|
|
|
After:
|
|
/# setpci -s 1:0.0 0x88.l - DevSta: CorrErr+
|
|
00012810
|
|
/# setpci -s 1:0.0 0x88.w=0x2810 - check DevCtl only write
|
|
/# setpci -s 1:0.0 0x88.l - CorErr remains set
|
|
00012810
|
|
/# setpci -s 1:0.0 0x88.w=0x281f - check DevCtl write works
|
|
/# setpci -s 1:0.0 0x88.l - devctl field updated
|
|
0001281f
|
|
/# setpci -s 1:0.0 0x8a.w=0xffff - clear DevSta
|
|
/# setpci -s 1:0.0 0x88.l - CorrErr now cleared
|
|
0000281f
|
|
/# setpci -s 1:0.0 0x88.w=0x2810 - restore DevCtl
|
|
/# setpci -s 1:0.0 0x88.l - check
|
|
00002810
|
|
|
|
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
|
|
---
|
|
drivers/pci/host/pci-mvebu.c | 43 ++++++++++++++++++++++++++-----------------
|
|
1 file changed, 26 insertions(+), 17 deletions(-)
|
|
|
|
diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c
|
|
index b6a096bc9422..0d9f3eae4315 100644
|
|
--- a/drivers/pci/host/pci-mvebu.c
|
|
+++ b/drivers/pci/host/pci-mvebu.c
|
|
@@ -254,15 +254,22 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port,
|
|
struct pci_bus *bus,
|
|
u32 devfn, int where, int size, u32 *val)
|
|
{
|
|
+ void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF;
|
|
+
|
|
mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where),
|
|
PCIE_CONF_ADDR_OFF);
|
|
|
|
- *val = mvebu_readl(port, PCIE_CONF_DATA_OFF);
|
|
-
|
|
- if (size == 1)
|
|
- *val = (*val >> (8 * (where & 3))) & 0xff;
|
|
- else if (size == 2)
|
|
- *val = (*val >> (8 * (where & 3))) & 0xffff;
|
|
+ switch (size) {
|
|
+ case 1:
|
|
+ *val = readb_relaxed(conf_data + (where & 3));
|
|
+ break;
|
|
+ case 2:
|
|
+ *val = readw_relaxed(conf_data + (where & 2));
|
|
+ break;
|
|
+ case 4:
|
|
+ *val = readl_relaxed(conf_data);
|
|
+ break;
|
|
+ }
|
|
|
|
return PCIBIOS_SUCCESSFUL;
|
|
}
|
|
@@ -271,22 +278,24 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port,
|
|
struct pci_bus *bus,
|
|
u32 devfn, int where, int size, u32 val)
|
|
{
|
|
- u32 _val, shift = 8 * (where & 3);
|
|
+ void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF;
|
|
|
|
mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where),
|
|
PCIE_CONF_ADDR_OFF);
|
|
- _val = mvebu_readl(port, PCIE_CONF_DATA_OFF);
|
|
|
|
- if (size == 4)
|
|
- _val = val;
|
|
- else if (size == 2)
|
|
- _val = (_val & ~(0xffff << shift)) | ((val & 0xffff) << shift);
|
|
- else if (size == 1)
|
|
- _val = (_val & ~(0xff << shift)) | ((val & 0xff) << shift);
|
|
- else
|
|
+ switch (size) {
|
|
+ case 1:
|
|
+ writeb(val, conf_data + (where & 3));
|
|
+ break;
|
|
+ case 2:
|
|
+ writew(val, conf_data + (where & 2));
|
|
+ break;
|
|
+ case 4:
|
|
+ writel(val, conf_data);
|
|
+ break;
|
|
+ default:
|
|
return PCIBIOS_BAD_REGISTER_NUMBER;
|
|
-
|
|
- mvebu_writel(port, _val, PCIE_CONF_DATA_OFF);
|
|
+ }
|
|
|
|
return PCIBIOS_SUCCESSFUL;
|
|
}
|
|
--
|
|
2.1.0
|
|
|