summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@armlinux.org.uk>2021-02-02 13:57:04 +0000
committerRussell King <rmk+kernel@armlinux.org.uk>2021-02-15 13:59:22 +0000
commit958379aa1d301afa1820984046c5351bce84dae1 (patch)
tree1fe44e663bc5dbd23d3c025aace4bfabd7064124
parent4c4b0caec3d4552d3dcc648c85c7713bd654f542 (diff)
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 <rmk+kernel@armlinux.org.uk>
-rw-r--r--drivers/pci/pci-bridge-emul.c52
-rw-r--r--drivers/pci/pci-bridge-emul.h15
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;