]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
PCI: thunder: Don't clobber read-only bits in bridge config registers
authorDavid Daney <david.daney@cavium.com>
Mon, 11 Apr 2016 23:29:32 +0000 (16:29 -0700)
committerBjorn Helgaas <bhelgaas@google.com>
Mon, 2 May 2016 17:31:53 +0000 (12:31 -0500)
The 32-bit addressing modes in the I/O and Prefetchable Memory registers
are required to be read-only.  Since the underlying access method allows
them to be set, emulate their read-only nature and always set them.

Signed-off-by: David Daney <david.daney@cavium.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
drivers/pci/host/pci-thunder-pem.c

index cabb92a514ac85788d0d684f8032cfab0a42af40..d4a7797614725d153776fc1c7ea079b6deeacbac 100644 (file)
@@ -153,11 +153,11 @@ static int thunder_pem_config_read(struct pci_bus *bus, unsigned int devfn,
  * reserved bits, this makes the code simpler and is OK as the bits
  * are not affected by writing zeros to them.
  */
-static u32 thunder_pem_bridge_w1c_bits(int where)
+static u32 thunder_pem_bridge_w1c_bits(u64 where_aligned)
 {
        u32 w1c_bits = 0;
 
-       switch (where & ~3) {
+       switch (where_aligned) {
        case 0x04: /* Command/Status */
        case 0x1c: /* Base and I/O Limit/Secondary Status */
                w1c_bits = 0xff000000;
@@ -184,12 +184,34 @@ static u32 thunder_pem_bridge_w1c_bits(int where)
        return w1c_bits;
 }
 
+/* Some bits must be written to one so they appear to be read-only. */
+static u32 thunder_pem_bridge_w1_bits(u64 where_aligned)
+{
+       u32 w1_bits;
+
+       switch (where_aligned) {
+       case 0x1c: /* I/O Base / I/O Limit, Secondary Status */
+               /* Force 32-bit I/O addressing. */
+               w1_bits = 0x0101;
+               break;
+       case 0x24: /* Prefetchable Memory Base / Prefetchable Memory Limit */
+               /* Force 64-bit addressing */
+               w1_bits = 0x00010001;
+               break;
+       default:
+               w1_bits = 0;
+               break;
+       }
+       return w1_bits;
+}
+
 static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
                                    int where, int size, u32 val)
 {
        struct gen_pci *pci = bus->sysdata;
        struct thunder_pem_pci *pem_pci;
        u64 write_val, read_val;
+       u64 where_aligned = where & ~3ull;
        u32 mask = 0;
 
        pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci);
@@ -205,8 +227,7 @@ static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
         */
        switch (size) {
        case 1:
-               read_val = where & ~3ull;
-               writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD);
+               writeq(where_aligned, pem_pci->pem_reg_base + PEM_CFG_RD);
                read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD);
                read_val >>= 32;
                mask = ~(0xff << (8 * (where & 3)));
@@ -215,8 +236,7 @@ static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
                val |= (u32)read_val;
                break;
        case 2:
-               read_val = where & ~3ull;
-               writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD);
+               writeq(where_aligned, pem_pci->pem_reg_base + PEM_CFG_RD);
                read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD);
                read_val >>= 32;
                mask = ~(0xffff << (8 * (where & 3)));
@@ -243,12 +263,18 @@ static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
                }
        }
 
+       /*
+        * Some bits must be read-only with value of one.  Since the
+        * access method allows these to be cleared if a zero is
+        * written, force them to one before writing.
+        */
+       val |= thunder_pem_bridge_w1_bits(where_aligned);
+
        /*
         * Low order bits are the config address, the high order 32
         * bits are the data to be written.
         */
-       write_val = where & ~3ull;
-       write_val |= (((u64)val) << 32);
+       write_val = (((u64)val) << 32) | where_aligned;
        writeq(write_val, pem_pci->pem_reg_base + PEM_CFG_WR);
        return PCIBIOS_SUCCESSFUL;
 }