From 958379aa1d301afa1820984046c5351bce84dae1 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 2 Feb 2021 13:57:04 +0000 Subject: PCI: pci-bridge-emul: add support for PCIe extended capabilities Add support for PCIe extended capabilities, which we just redirect to the emulating driver. Signed-off-by: Russell King --- drivers/pci/pci-bridge-emul.c | 52 +++++++++++++++++++++++++++++++------------ drivers/pci/pci-bridge-emul.h | 15 +++++++++++++ 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c index 9988078e7b0e..fbff7da94245 100644 --- a/drivers/pci/pci-bridge-emul.c +++ b/drivers/pci/pci-bridge-emul.c @@ -343,10 +343,16 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, read_op = bridge->ops->read_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; - } else { - /* Beyond our PCIe space */ + } else if (reg < PCI_CFG_SPACE_SIZE) { + /* Rest of PCI space not implemented */ *value = 0; return PCIBIOS_SUCCESSFUL; + } else { + /* PCIe extended capability space */ + reg -= PCI_CFG_SPACE_SIZE; + read_op = bridge->ops->read_ext; + cfgspace = NULL; + behavior = NULL; } if (read_op) @@ -354,15 +360,20 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, else ret = PCI_BRIDGE_EMUL_NOT_HANDLED; - if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED) - *value = le32_to_cpu(cfgspace[reg / 4]); + if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED) { + if (cfgspace) + *value = le32_to_cpu(cfgspace[reg / 4]); + else + *value = 0; + } /* * Make sure we never return any reserved bit with a value * different from 0. */ - *value &= behavior[reg / 4].ro | behavior[reg / 4].rw | - behavior[reg / 4].w1c; + if (behavior) + *value &= behavior[reg / 4].ro | behavior[reg / 4].rw | + behavior[reg / 4].w1c; if (size == 1) *value = (*value >> (8 * (where & 3))) & 0xff; @@ -404,8 +415,15 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, write_op = bridge->ops->write_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; - } else { + } else if (reg < PCI_CFG_SPACE_SIZE) { + /* Rest of PCI space not implemented */ return PCIBIOS_SUCCESSFUL; + } else { + /* PCIe extended capability space */ + reg -= PCI_CFG_SPACE_SIZE; + write_op = bridge->ops->write_ext; + cfgspace = NULL; + behavior = NULL; } shift = (where & 0x3) * 8; @@ -423,16 +441,22 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, if (ret != PCIBIOS_SUCCESSFUL) return ret; - /* Keep all bits, except the RW bits */ - new = old & (~mask | ~behavior[reg / 4].rw); + if (behavior) { + /* Keep all bits, except the RW bits */ + new = old & (~mask | ~behavior[reg / 4].rw); - /* Update the value of the RW bits */ - new |= (value << shift) & (behavior[reg / 4].rw & mask); + /* Update the value of the RW bits */ + new |= (value << shift) & (behavior[reg / 4].rw & mask); - /* Clear the W1C bits */ - new &= ~((value << shift) & (behavior[reg / 4].w1c & mask)); + /* Clear the W1C bits */ + new &= ~((value << shift) & (behavior[reg / 4].w1c & mask)); + } else { + new = old & ~mask; + new |= (value << shift) & mask; + } - cfgspace[reg / 4] = cpu_to_le32(new); + if (cfgspace) + cfgspace[reg / 4] = cpu_to_le32(new); if (write_op) write_op(bridge, reg, old, new, mask); diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h index b31883022a8e..5f64560aaa26 100644 --- a/drivers/pci/pci-bridge-emul.h +++ b/drivers/pci/pci-bridge-emul.h @@ -90,6 +90,14 @@ struct pci_bridge_emul_ops { */ pci_bridge_emul_read_status_t (*read_pcie)(struct pci_bridge_emul *bridge, int reg, u32 *value); + + /* + * Same as ->read_base(), except it is for reading from the + * PCIe extended capability configuration space. + */ + pci_bridge_emul_read_status_t (*read_ext)(struct pci_bridge_emul *bridge, + int reg, u32 *value); + /* * Called when writing to the regular PCI bridge configuration * space. old is the current value, new is the new value being @@ -105,6 +113,13 @@ struct pci_bridge_emul_ops { */ void (*write_pcie)(struct pci_bridge_emul *bridge, int reg, u32 old, u32 new, u32 mask); + + /* + * Same as ->write_base(), except it is for writing from the + * PCIe extended capability configuration space. + */ + void (*write_ext)(struct pci_bridge_emul *bridge, int reg, + u32 old, u32 new, u32 mask); }; struct pci_bridge_reg_behavior; -- cgit