Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI: thunder: Add PCIe host driver for ThunderX processors

The root complexes used to access off-chip PCIe devices (called PEM units
in the hardware manuals) on some Cavium ThunderX processors require quirky
access methods for the config space of the PCIe bridge.

Add a driver to provide these config space accessor functions. Use the
pci-host-common code to configure the PCI machinery.

Signed-off-by: David Daney <david.daney@cavium.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Arnd Bergmann <arnd@arndb.de>

authored by

David Daney and committed by
Bjorn Helgaas
f12b76e5 4e64dbe2

+405
+43
Documentation/devicetree/bindings/pci/pci-thunder-pem.txt
··· 1 + * ThunderX PEM PCIe host controller 2 + 3 + Firmware-initialized PCI host controller found on some Cavium 4 + ThunderX processors. 5 + 6 + The properties and their meanings are identical to those described in 7 + host-generic-pci.txt except as listed below. 8 + 9 + Properties of the host controller node that differ from 10 + host-generic-pci.txt: 11 + 12 + - compatible : Must be "cavium,pci-host-thunder-pem" 13 + 14 + - reg : Two entries: First the configuration space for down 15 + stream devices base address and size, as accessed 16 + from the parent bus. Second, the register bank of 17 + the PEM device PCIe bridge. 18 + 19 + Example: 20 + 21 + pci@87e0,c2000000 { 22 + compatible = "cavium,pci-host-thunder-pem"; 23 + device_type = "pci"; 24 + msi-parent = <&its>; 25 + msi-map = <0 &its 0x10000 0x10000>; 26 + bus-range = <0x8f 0xc7>; 27 + #size-cells = <2>; 28 + #address-cells = <3>; 29 + 30 + reg = <0x8880 0x8f000000 0x0 0x39000000>, /* Configuration space */ 31 + <0x87e0 0xc2000000 0x0 0x00010000>; /* PEM space */ 32 + ranges = <0x01000000 0x00 0x00020000 0x88b0 0x00020000 0x00 0x00010000>, /* I/O */ 33 + <0x03000000 0x00 0x10000000 0x8890 0x10000000 0x0f 0xf0000000>, /* mem64 */ 34 + <0x43000000 0x10 0x00000000 0x88a0 0x00000000 0x10 0x00000000>, /* mem64-pref */ 35 + <0x03000000 0x87e0 0xc2f00000 0x87e0 0xc2000000 0x00 0x00100000>; /* mem64 PEM BAR4 */ 36 + 37 + #interrupt-cells = <1>; 38 + interrupt-map-mask = <0 0 0 7>; 39 + interrupt-map = <0 0 0 1 &gic0 0 0 0 24 4>, /* INTA */ 40 + <0 0 0 2 &gic0 0 0 0 25 4>, /* INTB */ 41 + <0 0 0 3 &gic0 0 0 0 26 4>, /* INTC */ 42 + <0 0 0 4 &gic0 0 0 0 27 4>; /* INTD */ 43 + };
+8
MAINTAINERS
··· 8419 8419 S: Maintained 8420 8420 F: drivers/pci/host/*qcom* 8421 8421 8422 + PCIE DRIVER FOR CAVIUM THUNDERX 8423 + M: David Daney <david.daney@cavium.com> 8424 + L: linux-pci@vger.kernel.org 8425 + L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) 8426 + S: Supported 8427 + F: Documentation/devicetree/bindings/pci/pci-thunder-* 8428 + F: drivers/pci/host/pci-thunder-* 8429 + 8422 8430 PCMCIA SUBSYSTEM 8423 8431 P: Linux PCMCIA Team 8424 8432 L: linux-pcmcia@lists.infradead.org
+7
drivers/pci/host/Kconfig
··· 195 195 PCIe controller uses the Designware core plus Qualcomm-specific 196 196 hardware wrappers. 197 197 198 + config PCI_HOST_THUNDER_PEM 199 + bool "Cavium Thunder PCIe controller to off-chip devices" 200 + depends on OF && ARM64 201 + select PCI_HOST_COMMON 202 + help 203 + Say Y here if you want PCIe support for CN88XX Cavium Thunder SoCs. 204 + 198 205 endmenu
+1
drivers/pci/host/Makefile
··· 23 23 obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o 24 24 obj-$(CONFIG_PCI_HISI) += pcie-hisi.o 25 25 obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o 26 + obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
+346
drivers/pci/host/pci-thunder-pem.c
··· 1 + /* 2 + * This program is free software; you can redistribute it and/or modify 3 + * it under the terms of the GNU General Public License version 2 as 4 + * published by the Free Software Foundation. 5 + * 6 + * This program is distributed in the hope that it will be useful, 7 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 8 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 + * GNU General Public License for more details. 10 + * 11 + * You should have received a copy of the GNU General Public License 12 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 13 + * 14 + * Copyright (C) 2015 - 2016 Cavium, Inc. 15 + */ 16 + 17 + #include <linux/kernel.h> 18 + #include <linux/module.h> 19 + #include <linux/of_address.h> 20 + #include <linux/of_pci.h> 21 + #include <linux/platform_device.h> 22 + 23 + #include "pci-host-common.h" 24 + 25 + #define PEM_CFG_WR 0x28 26 + #define PEM_CFG_RD 0x30 27 + 28 + struct thunder_pem_pci { 29 + struct gen_pci gen_pci; 30 + u32 ea_entry[3]; 31 + void __iomem *pem_reg_base; 32 + }; 33 + 34 + static void __iomem *thunder_pem_map_bus(struct pci_bus *bus, 35 + unsigned int devfn, int where) 36 + { 37 + struct gen_pci *pci = bus->sysdata; 38 + resource_size_t idx = bus->number - pci->cfg.bus_range->start; 39 + 40 + return pci->cfg.win[idx] + ((devfn << 16) | where); 41 + } 42 + 43 + static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn, 44 + int where, int size, u32 *val) 45 + { 46 + u64 read_val; 47 + struct thunder_pem_pci *pem_pci; 48 + struct gen_pci *pci = bus->sysdata; 49 + 50 + pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci); 51 + 52 + if (devfn != 0 || where >= 2048) { 53 + *val = ~0; 54 + return PCIBIOS_DEVICE_NOT_FOUND; 55 + } 56 + 57 + /* 58 + * 32-bit accesses only. Write the address to the low order 59 + * bits of PEM_CFG_RD, then trigger the read by reading back. 60 + * The config data lands in the upper 32-bits of PEM_CFG_RD. 61 + */ 62 + read_val = where & ~3ull; 63 + writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD); 64 + read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); 65 + read_val >>= 32; 66 + 67 + /* 68 + * The config space contains some garbage, fix it up. Also 69 + * synthesize an EA capability for the BAR used by MSI-X. 70 + */ 71 + switch (where & ~3) { 72 + case 0x40: 73 + read_val &= 0xffff00ff; 74 + read_val |= 0x00007000; /* Skip MSI CAP */ 75 + break; 76 + case 0x70: /* Express Cap */ 77 + /* PME interrupt on vector 2*/ 78 + read_val |= (2u << 25); 79 + break; 80 + case 0xb0: /* MSI-X Cap */ 81 + /* TableSize=4, Next Cap is EA */ 82 + read_val &= 0xc00000ff; 83 + read_val |= 0x0003bc00; 84 + break; 85 + case 0xb4: 86 + /* Table offset=0, BIR=0 */ 87 + read_val = 0x00000000; 88 + break; 89 + case 0xb8: 90 + /* BPA offset=0xf0000, BIR=0 */ 91 + read_val = 0x000f0000; 92 + break; 93 + case 0xbc: 94 + /* EA, 1 entry, no next Cap */ 95 + read_val = 0x00010014; 96 + break; 97 + case 0xc0: 98 + /* DW2 for type-1 */ 99 + read_val = 0x00000000; 100 + break; 101 + case 0xc4: 102 + /* Entry BEI=0, PP=0x00, SP=0xff, ES=3 */ 103 + read_val = 0x80ff0003; 104 + break; 105 + case 0xc8: 106 + read_val = pem_pci->ea_entry[0]; 107 + break; 108 + case 0xcc: 109 + read_val = pem_pci->ea_entry[1]; 110 + break; 111 + case 0xd0: 112 + read_val = pem_pci->ea_entry[2]; 113 + break; 114 + default: 115 + break; 116 + } 117 + read_val >>= (8 * (where & 3)); 118 + switch (size) { 119 + case 1: 120 + read_val &= 0xff; 121 + break; 122 + case 2: 123 + read_val &= 0xffff; 124 + break; 125 + default: 126 + break; 127 + } 128 + *val = read_val; 129 + return PCIBIOS_SUCCESSFUL; 130 + } 131 + 132 + static int thunder_pem_config_read(struct pci_bus *bus, unsigned int devfn, 133 + int where, int size, u32 *val) 134 + { 135 + struct gen_pci *pci = bus->sysdata; 136 + 137 + if (bus->number < pci->cfg.bus_range->start || 138 + bus->number > pci->cfg.bus_range->end) 139 + return PCIBIOS_DEVICE_NOT_FOUND; 140 + 141 + /* 142 + * The first device on the bus is the PEM PCIe bridge. 143 + * Special case its config access. 144 + */ 145 + if (bus->number == pci->cfg.bus_range->start) 146 + return thunder_pem_bridge_read(bus, devfn, where, size, val); 147 + 148 + return pci_generic_config_read(bus, devfn, where, size, val); 149 + } 150 + 151 + /* 152 + * Some of the w1c_bits below also include read-only or non-writable 153 + * reserved bits, this makes the code simpler and is OK as the bits 154 + * are not affected by writing zeros to them. 155 + */ 156 + static u32 thunder_pem_bridge_w1c_bits(int where) 157 + { 158 + u32 w1c_bits = 0; 159 + 160 + switch (where & ~3) { 161 + case 0x04: /* Command/Status */ 162 + case 0x1c: /* Base and I/O Limit/Secondary Status */ 163 + w1c_bits = 0xff000000; 164 + break; 165 + case 0x44: /* Power Management Control and Status */ 166 + w1c_bits = 0xfffffe00; 167 + break; 168 + case 0x78: /* Device Control/Device Status */ 169 + case 0x80: /* Link Control/Link Status */ 170 + case 0x88: /* Slot Control/Slot Status */ 171 + case 0x90: /* Root Status */ 172 + case 0xa0: /* Link Control 2 Registers/Link Status 2 */ 173 + w1c_bits = 0xffff0000; 174 + break; 175 + case 0x104: /* Uncorrectable Error Status */ 176 + case 0x110: /* Correctable Error Status */ 177 + case 0x130: /* Error Status */ 178 + case 0x160: /* Link Control 4 */ 179 + w1c_bits = 0xffffffff; 180 + break; 181 + default: 182 + break; 183 + } 184 + return w1c_bits; 185 + } 186 + 187 + static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn, 188 + int where, int size, u32 val) 189 + { 190 + struct gen_pci *pci = bus->sysdata; 191 + struct thunder_pem_pci *pem_pci; 192 + u64 write_val, read_val; 193 + u32 mask = 0; 194 + 195 + pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci); 196 + 197 + if (devfn != 0 || where >= 2048) 198 + return PCIBIOS_DEVICE_NOT_FOUND; 199 + 200 + /* 201 + * 32-bit accesses only. If the write is for a size smaller 202 + * than 32-bits, we must first read the 32-bit value and merge 203 + * in the desired bits and then write the whole 32-bits back 204 + * out. 205 + */ 206 + switch (size) { 207 + case 1: 208 + read_val = where & ~3ull; 209 + writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD); 210 + read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); 211 + read_val >>= 32; 212 + mask = ~(0xff << (8 * (where & 3))); 213 + read_val &= mask; 214 + val = (val & 0xff) << (8 * (where & 3)); 215 + val |= (u32)read_val; 216 + break; 217 + case 2: 218 + read_val = where & ~3ull; 219 + writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD); 220 + read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); 221 + read_val >>= 32; 222 + mask = ~(0xffff << (8 * (where & 3))); 223 + read_val &= mask; 224 + val = (val & 0xffff) << (8 * (where & 3)); 225 + val |= (u32)read_val; 226 + break; 227 + default: 228 + break; 229 + } 230 + 231 + /* 232 + * By expanding the write width to 32 bits, we may 233 + * inadvertently hit some W1C bits that were not intended to 234 + * be written. Calculate the mask that must be applied to the 235 + * data to be written to avoid these cases. 236 + */ 237 + if (mask) { 238 + u32 w1c_bits = thunder_pem_bridge_w1c_bits(where); 239 + 240 + if (w1c_bits) { 241 + mask &= w1c_bits; 242 + val &= ~mask; 243 + } 244 + } 245 + 246 + /* 247 + * Low order bits are the config address, the high order 32 248 + * bits are the data to be written. 249 + */ 250 + write_val = where & ~3ull; 251 + write_val |= (((u64)val) << 32); 252 + writeq(write_val, pem_pci->pem_reg_base + PEM_CFG_WR); 253 + return PCIBIOS_SUCCESSFUL; 254 + } 255 + 256 + static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn, 257 + int where, int size, u32 val) 258 + { 259 + struct gen_pci *pci = bus->sysdata; 260 + 261 + if (bus->number < pci->cfg.bus_range->start || 262 + bus->number > pci->cfg.bus_range->end) 263 + return PCIBIOS_DEVICE_NOT_FOUND; 264 + /* 265 + * The first device on the bus is the PEM PCIe bridge. 266 + * Special case its config access. 267 + */ 268 + if (bus->number == pci->cfg.bus_range->start) 269 + return thunder_pem_bridge_write(bus, devfn, where, size, val); 270 + 271 + 272 + return pci_generic_config_write(bus, devfn, where, size, val); 273 + } 274 + 275 + static struct gen_pci_cfg_bus_ops thunder_pem_bus_ops = { 276 + .bus_shift = 24, 277 + .ops = { 278 + .map_bus = thunder_pem_map_bus, 279 + .read = thunder_pem_config_read, 280 + .write = thunder_pem_config_write, 281 + } 282 + }; 283 + 284 + static const struct of_device_id thunder_pem_of_match[] = { 285 + { .compatible = "cavium,pci-host-thunder-pem", 286 + .data = &thunder_pem_bus_ops }, 287 + 288 + { }, 289 + }; 290 + MODULE_DEVICE_TABLE(of, thunder_pem_of_match); 291 + 292 + static int thunder_pem_probe(struct platform_device *pdev) 293 + { 294 + struct device *dev = &pdev->dev; 295 + const struct of_device_id *of_id; 296 + resource_size_t bar4_start; 297 + struct resource *res_pem; 298 + struct thunder_pem_pci *pem_pci; 299 + 300 + pem_pci = devm_kzalloc(dev, sizeof(*pem_pci), GFP_KERNEL); 301 + if (!pem_pci) 302 + return -ENOMEM; 303 + 304 + of_id = of_match_node(thunder_pem_of_match, dev->of_node); 305 + pem_pci->gen_pci.cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data; 306 + 307 + /* 308 + * The second register range is the PEM bridge to the PCIe 309 + * bus. It has a different config access method than those 310 + * devices behind the bridge. 311 + */ 312 + res_pem = platform_get_resource(pdev, IORESOURCE_MEM, 1); 313 + if (!res_pem) { 314 + dev_err(dev, "missing \"reg[1]\"property\n"); 315 + return -EINVAL; 316 + } 317 + 318 + pem_pci->pem_reg_base = devm_ioremap(dev, res_pem->start, 0x10000); 319 + if (!pem_pci->pem_reg_base) 320 + return -ENOMEM; 321 + 322 + /* 323 + * The MSI-X BAR for the PEM and AER interrupts is located at 324 + * a fixed offset from the PEM register base. Generate a 325 + * fragment of the synthesized Enhanced Allocation capability 326 + * structure here for the BAR. 327 + */ 328 + bar4_start = res_pem->start + 0xf00000; 329 + pem_pci->ea_entry[0] = (u32)bar4_start | 2; 330 + pem_pci->ea_entry[1] = (u32)(res_pem->end - bar4_start) & ~3u; 331 + pem_pci->ea_entry[2] = (u32)(bar4_start >> 32); 332 + 333 + return pci_host_common_probe(pdev, &pem_pci->gen_pci); 334 + } 335 + 336 + static struct platform_driver thunder_pem_driver = { 337 + .driver = { 338 + .name = KBUILD_MODNAME, 339 + .of_match_table = thunder_pem_of_match, 340 + }, 341 + .probe = thunder_pem_probe, 342 + }; 343 + module_platform_driver(thunder_pem_driver); 344 + 345 + MODULE_DESCRIPTION("Thunder PEM PCIe host driver"); 346 + MODULE_LICENSE("GPL v2");