Skip to content

Commit c0bd419

Browse files
Russell KingLorenzo Pieralisi
authored andcommitted
PCI: pci-bridge-emul: Add support for PCIe extended capabilities
Add support for PCIe extended capabilities, which we just redirect to the emulating driver. [pali: Fix writing new value with W1C bits] Link: https://lore.kernel.org/r/20220222155030.988-3-pali@kernel.org Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> Signed-off-by: Pali Rohár <pali@kernel.org> Signed-off-by: Marek Behún <kabel@kernel.org> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
1 parent c453bf6 commit c0bd419

2 files changed

Lines changed: 67 additions & 25 deletions

File tree

drivers/pci/pci-bridge-emul.c

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -437,26 +437,37 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
437437
read_op = bridge->ops->read_pcie;
438438
cfgspace = (__le32 *) &bridge->pcie_conf;
439439
behavior = bridge->pcie_cap_regs_behavior;
440-
} else {
441-
/* Beyond our PCIe space */
440+
} else if (reg < PCI_CFG_SPACE_SIZE) {
441+
/* Rest of PCI space not implemented */
442442
*value = 0;
443443
return PCIBIOS_SUCCESSFUL;
444+
} else {
445+
/* PCIe extended capability space */
446+
reg -= PCI_CFG_SPACE_SIZE;
447+
read_op = bridge->ops->read_ext;
448+
cfgspace = NULL;
449+
behavior = NULL;
444450
}
445451

446452
if (read_op)
447453
ret = read_op(bridge, reg, value);
448454
else
449455
ret = PCI_BRIDGE_EMUL_NOT_HANDLED;
450456

451-
if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED)
452-
*value = le32_to_cpu(cfgspace[reg / 4]);
457+
if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED) {
458+
if (cfgspace)
459+
*value = le32_to_cpu(cfgspace[reg / 4]);
460+
else
461+
*value = 0;
462+
}
453463

454464
/*
455465
* Make sure we never return any reserved bit with a value
456466
* different from 0.
457467
*/
458-
*value &= behavior[reg / 4].ro | behavior[reg / 4].rw |
459-
behavior[reg / 4].w1c;
468+
if (behavior)
469+
*value &= behavior[reg / 4].ro | behavior[reg / 4].rw |
470+
behavior[reg / 4].w1c;
460471

461472
if (size == 1)
462473
*value = (*value >> (8 * (where & 3))) & 0xff;
@@ -502,8 +513,15 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
502513
write_op = bridge->ops->write_pcie;
503514
cfgspace = (__le32 *) &bridge->pcie_conf;
504515
behavior = bridge->pcie_cap_regs_behavior;
505-
} else {
516+
} else if (reg < PCI_CFG_SPACE_SIZE) {
517+
/* Rest of PCI space not implemented */
506518
return PCIBIOS_SUCCESSFUL;
519+
} else {
520+
/* PCIe extended capability space */
521+
reg -= PCI_CFG_SPACE_SIZE;
522+
write_op = bridge->ops->write_ext;
523+
cfgspace = NULL;
524+
behavior = NULL;
507525
}
508526

509527
shift = (where & 0x3) * 8;
@@ -517,29 +535,38 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
517535
else
518536
return PCIBIOS_BAD_REGISTER_NUMBER;
519537

520-
/* Keep all bits, except the RW bits */
521-
new = old & (~mask | ~behavior[reg / 4].rw);
538+
if (behavior) {
539+
/* Keep all bits, except the RW bits */
540+
new = old & (~mask | ~behavior[reg / 4].rw);
522541

523-
/* Update the value of the RW bits */
524-
new |= (value << shift) & (behavior[reg / 4].rw & mask);
542+
/* Update the value of the RW bits */
543+
new |= (value << shift) & (behavior[reg / 4].rw & mask);
525544

526-
/* Clear the W1C bits */
527-
new &= ~((value << shift) & (behavior[reg / 4].w1c & mask));
545+
/* Clear the W1C bits */
546+
new &= ~((value << shift) & (behavior[reg / 4].w1c & mask));
547+
} else {
548+
new = old & ~mask;
549+
new |= (value << shift) & mask;
550+
}
528551

529-
/* Save the new value with the cleared W1C bits into the cfgspace */
530-
cfgspace[reg / 4] = cpu_to_le32(new);
552+
if (cfgspace) {
553+
/* Save the new value with the cleared W1C bits into the cfgspace */
554+
cfgspace[reg / 4] = cpu_to_le32(new);
555+
}
531556

532-
/*
533-
* Clear the W1C bits not specified by the write mask, so that the
534-
* write_op() does not clear them.
535-
*/
536-
new &= ~(behavior[reg / 4].w1c & ~mask);
557+
if (behavior) {
558+
/*
559+
* Clear the W1C bits not specified by the write mask, so that the
560+
* write_op() does not clear them.
561+
*/
562+
new &= ~(behavior[reg / 4].w1c & ~mask);
537563

538-
/*
539-
* Set the W1C bits specified by the write mask, so that write_op()
540-
* knows about that they are to be cleared.
541-
*/
542-
new |= (value << shift) & (behavior[reg / 4].w1c & mask);
564+
/*
565+
* Set the W1C bits specified by the write mask, so that write_op()
566+
* knows about that they are to be cleared.
567+
*/
568+
new |= (value << shift) & (behavior[reg / 4].w1c & mask);
569+
}
543570

544571
if (write_op)
545572
write_op(bridge, reg, old, new, mask);

drivers/pci/pci-bridge-emul.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ struct pci_bridge_emul_ops {
9090
*/
9191
pci_bridge_emul_read_status_t (*read_pcie)(struct pci_bridge_emul *bridge,
9292
int reg, u32 *value);
93+
94+
/*
95+
* Same as ->read_base(), except it is for reading from the
96+
* PCIe extended capability configuration space.
97+
*/
98+
pci_bridge_emul_read_status_t (*read_ext)(struct pci_bridge_emul *bridge,
99+
int reg, u32 *value);
100+
93101
/*
94102
* Called when writing to the regular PCI bridge configuration
95103
* space. old is the current value, new is the new value being
@@ -105,6 +113,13 @@ struct pci_bridge_emul_ops {
105113
*/
106114
void (*write_pcie)(struct pci_bridge_emul *bridge, int reg,
107115
u32 old, u32 new, u32 mask);
116+
117+
/*
118+
* Same as ->write_base(), except it is for writing from the
119+
* PCIe extended capability configuration space.
120+
*/
121+
void (*write_ext)(struct pci_bridge_emul *bridge, int reg,
122+
u32 old, u32 new, u32 mask);
108123
};
109124

110125
struct pci_bridge_reg_behavior;

0 commit comments

Comments
 (0)