PCI: Warn on possible RW1C corruption for sub-32 bit config writes
authorBjorn Helgaas <bhelgaas@google.com>
Mon, 31 Oct 2016 21:00:01 +0000 (16:00 -0500)
committerBjorn Helgaas <bhelgaas@google.com>
Mon, 21 Nov 2016 22:25:39 +0000 (16:25 -0600)
Hardware that supports only 32-bit config writes is not spec-compliant.
For example, if software performs a 16-bit write, we must do a 32-bit read,
merge in the 16 bits we intend to write, followed by a 32-bit write.  If
the 16 bits we *don't* intend to write happen to have any RW1C (write-one-
to-clear) bits set, we just inadvertently cleared something we shouldn't
have.

Add a rate-limited warning when we do sub-32 bit config writes.  Remove
similar probe-time warnings from some of the affected host bridge drivers.

Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Enthusiastically-Acked-by: Russell King <rmk+kernel@armlinux.org.uk>
Acked-by: Shawn Lin <shawn.lin@rock-chips.com> # rockchip
Acked-by: Thierry Reding <treding@nvidia.com>
drivers/pci/access.c
drivers/pci/host/pcie-hisi.c
drivers/pci/host/pcie-rockchip.c

index d11cdbb8fba3edab6d0bfc69490c72b0c40394dd..db239547fefd0905b416db24b794925b816a20a9 100644 (file)
@@ -142,10 +142,22 @@ int pci_generic_config_write32(struct pci_bus *bus, unsigned int devfn,
        if (size == 4) {
                writel(val, addr);
                return PCIBIOS_SUCCESSFUL;
-       } else {
-               mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8));
        }
 
+       /*
+        * In general, hardware that supports only 32-bit writes on PCI is
+        * not spec-compliant.  For example, software may perform a 16-bit
+        * write.  If the hardware only supports 32-bit accesses, we must
+        * do a 32-bit read, merge in the 16 bits we intend to write,
+        * followed by a 32-bit write.  If the 16 bits we *don't* intend to
+        * write happen to have any RW1C (write-one-to-clear) bits set, we
+        * just inadvertently cleared something we shouldn't have.
+        */
+       dev_warn_ratelimited(&bus->dev, "%d-byte config write to %04x:%02x:%02x.%d offset %#x may corrupt adjacent RW1C bits\n",
+                            size, pci_domain_nr(bus), bus->number,
+                            PCI_SLOT(devfn), PCI_FUNC(devfn), where);
+
+       mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8));
        tmp = readl(addr) & mask;
        tmp |= val << ((where & 0x3) * 8);
        writel(tmp, addr);
index 56154c25980c6d83b1a155fc07e7520e8cee3ac1..5b5901d361d8fef2b763a86e9c352525373b4b64 100644 (file)
@@ -194,8 +194,6 @@ static int hisi_pcie_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       dev_warn(dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n");
-
        return 0;
 }
 
index e0b22dab9b7ac37c81d380bfe00751f4496f4516..6419d8ca4a844fb14dfdf33decebaa42c50937e8 100644 (file)
@@ -1187,9 +1187,6 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
                pcie_bus_configure_settings(child);
 
        pci_bus_add_devices(bus);
-
-       dev_warn(dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n");
-
        return err;
 
 err_vpcie: