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

PCI: pci-bridge-emul: Create per-bridge copy of register behavior

The behavior of the different registers of the PCI-to-PCI bridge is
currently encoded in two global arrays, shared by all instances of
PCI-to-PCI bridge emulation.

However, we will need to tweak the behavior on a per-bridge basis, to
accommodate for different capabilities of the platforms where this
code is used. In preparation for this, create a per-bridge copy of the
register behavior arrays, so that they can later be tweaked on a
per-bridge basis.

Fixes: 1f08673eef123 ("PCI: mvebu: Convert to PCI emulated bridge config space")
Reported-by: Luís Mendes <luis.p.mendes@gmail.com>
Reported-by: Leigh Brown <leigh@solinno.co.uk>
Tested-by: Leigh Brown <leigh@solinno.co.uk>
Tested-by: Luis Mendes <luis.p.mendes@gmail.com>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: stable@vger.kernel.org
Cc: Luís Mendes <luis.p.mendes@gmail.com>
Cc: Leigh Brown <leigh@solinno.co.uk>

authored by

Thomas Petazzoni and committed by
Lorenzo Pieralisi
59f81c35 bfeffd15

+60 -28
+53 -27
drivers/pci/pci-bridge-emul.c
··· 24 24 #define PCI_CAP_PCIE_START PCI_BRIDGE_CONF_END 25 25 #define PCI_CAP_PCIE_END (PCI_CAP_PCIE_START + PCI_EXP_SLTSTA2 + 2) 26 26 27 - /* 28 - * Initialize a pci_bridge_emul structure to represent a fake PCI 29 - * bridge configuration space. The caller needs to have initialized 30 - * the PCI configuration space with whatever values make sense 31 - * (typically at least vendor, device, revision), the ->ops pointer, 32 - * and optionally ->data and ->has_pcie. 33 - */ 34 - void pci_bridge_emul_init(struct pci_bridge_emul *bridge) 35 - { 36 - bridge->conf.class_revision |= PCI_CLASS_BRIDGE_PCI << 16; 37 - bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE; 38 - bridge->conf.cache_line_size = 0x10; 39 - bridge->conf.status = PCI_STATUS_CAP_LIST; 40 - 41 - if (bridge->has_pcie) { 42 - bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START; 43 - bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP; 44 - /* Set PCIe v2, root port, slot support */ 45 - bridge->pcie_conf.cap = PCI_EXP_TYPE_ROOT_PORT << 4 | 2 | 46 - PCI_EXP_FLAGS_SLOT; 47 - } 48 - } 49 - 50 27 struct pci_bridge_reg_behavior { 51 28 /* Read-only bits */ 52 29 u32 ro; ··· 261 284 }; 262 285 263 286 /* 287 + * Initialize a pci_bridge_emul structure to represent a fake PCI 288 + * bridge configuration space. The caller needs to have initialized 289 + * the PCI configuration space with whatever values make sense 290 + * (typically at least vendor, device, revision), the ->ops pointer, 291 + * and optionally ->data and ->has_pcie. 292 + */ 293 + int pci_bridge_emul_init(struct pci_bridge_emul *bridge) 294 + { 295 + bridge->conf.class_revision |= PCI_CLASS_BRIDGE_PCI << 16; 296 + bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE; 297 + bridge->conf.cache_line_size = 0x10; 298 + bridge->conf.status = PCI_STATUS_CAP_LIST; 299 + bridge->pci_regs_behavior = kmemdup(pci_regs_behavior, 300 + sizeof(pci_regs_behavior), 301 + GFP_KERNEL); 302 + if (!bridge->pci_regs_behavior) 303 + return -ENOMEM; 304 + 305 + if (bridge->has_pcie) { 306 + bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START; 307 + bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP; 308 + /* Set PCIe v2, root port, slot support */ 309 + bridge->pcie_conf.cap = PCI_EXP_TYPE_ROOT_PORT << 4 | 2 | 310 + PCI_EXP_FLAGS_SLOT; 311 + bridge->pcie_cap_regs_behavior = 312 + kmemdup(pcie_cap_regs_behavior, 313 + sizeof(pcie_cap_regs_behavior), 314 + GFP_KERNEL); 315 + if (!bridge->pcie_cap_regs_behavior) { 316 + kfree(bridge->pci_regs_behavior); 317 + return -ENOMEM; 318 + } 319 + } 320 + 321 + return 0; 322 + } 323 + 324 + /* 325 + * Cleanup a pci_bridge_emul structure that was previously initilized 326 + * using pci_bridge_emul_init(). 327 + */ 328 + void pci_bridge_emul_cleanup(struct pci_bridge_emul *bridge) 329 + { 330 + if (bridge->has_pcie) 331 + kfree(bridge->pcie_cap_regs_behavior); 332 + kfree(bridge->pci_regs_behavior); 333 + } 334 + 335 + /* 264 336 * Should be called by the PCI controller driver when reading the PCI 265 337 * configuration space of the fake bridge. It will call back the 266 338 * ->ops->read_base or ->ops->read_pcie operations. ··· 338 312 reg -= PCI_CAP_PCIE_START; 339 313 read_op = bridge->ops->read_pcie; 340 314 cfgspace = (u32 *) &bridge->pcie_conf; 341 - behavior = pcie_cap_regs_behavior; 315 + behavior = bridge->pcie_cap_regs_behavior; 342 316 } else { 343 317 read_op = bridge->ops->read_base; 344 318 cfgspace = (u32 *) &bridge->conf; 345 - behavior = pci_regs_behavior; 319 + behavior = bridge->pci_regs_behavior; 346 320 } 347 321 348 322 if (read_op) ··· 409 383 reg -= PCI_CAP_PCIE_START; 410 384 write_op = bridge->ops->write_pcie; 411 385 cfgspace = (u32 *) &bridge->pcie_conf; 412 - behavior = pcie_cap_regs_behavior; 386 + behavior = bridge->pcie_cap_regs_behavior; 413 387 } else { 414 388 write_op = bridge->ops->write_base; 415 389 cfgspace = (u32 *) &bridge->conf; 416 - behavior = pci_regs_behavior; 390 + behavior = bridge->pci_regs_behavior; 417 391 } 418 392 419 393 /* Keep all bits, except the RW bits */
+7 -1
drivers/pci/pci-bridge-emul.h
··· 107 107 u32 old, u32 new, u32 mask); 108 108 }; 109 109 110 + struct pci_bridge_reg_behavior; 111 + 110 112 struct pci_bridge_emul { 111 113 struct pci_bridge_emul_conf conf; 112 114 struct pci_bridge_emul_pcie_conf pcie_conf; 113 115 struct pci_bridge_emul_ops *ops; 116 + struct pci_bridge_reg_behavior *pci_regs_behavior; 117 + struct pci_bridge_reg_behavior *pcie_cap_regs_behavior; 114 118 void *data; 115 119 bool has_pcie; 116 120 }; 117 121 118 - void pci_bridge_emul_init(struct pci_bridge_emul *bridge); 122 + int pci_bridge_emul_init(struct pci_bridge_emul *bridge); 123 + void pci_bridge_emul_cleanup(struct pci_bridge_emul *bridge); 124 + 119 125 int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, 120 126 int size, u32 *value); 121 127 int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,