diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2022-03-23 01:16:25 +0300 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2022-03-23 01:16:25 +0300 |
commit | 9b2c25fa1299897d19f3befd9437055ee4b3213b (patch) | |
tree | 84310c97261bc2f9d76da55037c8bc53f5ea4e75 /drivers/pci/pci-bridge-emul.c | |
parent | 0888e08938e54b14b4176cd92e527c61ef68e4de (diff) | |
parent | ec075262648f396d65e03500b2a513ed69ba38d1 (diff) | |
download | linux-9b2c25fa1299897d19f3befd9437055ee4b3213b.tar.xz |
Merge branch 'remotes/lorenzo/pci/mvebu'
- Add Pali Rohár as pci-mvebu.c maintainer (Pali Rohár)
- Make struct pci_bridge_emul_ops const (Pali Rohár)
- Rename PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR to
PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD since it doesn't apply to BARs (Pali
Rohár)
- Add new flag PCI_BRIDGE_EMUL_NO_IO_FORWARD for bridges that don't support
IO forwarding (Pali Rohár)
- Add Kconfig help text for CONFIG_PCI_MVEBU (Pali Rohár)
- Remove duplicate nports assignment (Pali Rohár)
- Set PCI_BRIDGE_EMUL_NO_IO_FORWARD when IO is unsupported (Pali Rohár)
- Initialize vendor, device and revision of emulated bridge (Pali Rohár)
- Fix Data Link Layer Link Active reporting on emulated bridge (Pali Rohár)
- Rearrange tests in bridge emulation for easier maintenance (Russell King)
- Add emulated bridge support for PCIe extended capabilities (Russell King)
- Add emulated bridge support for bridge Subsystem Vendor ID capability
(Pali Rohár)
- Configure Maximum Link Width based on DT "num-lanes" property (Pali
Rohár)
- Emulate bridge Subsystem Vendor ID capability (Pali Rohár)
- Emulate AER Capability (Pali Rohár)
- Use PCI core bridge->ops and bridge->child_ops to separate config
accesses to Root Port vs downstream devices (Pali Rohár)
- Unmask all INTx interrupts; they're reported via a single shared GIC
source (Pali Rohár)
- Add INTx support (Pali Rohár)
* remotes/lorenzo/pci/mvebu:
PCI: mvebu: Implement support for legacy INTx interrupts
PCI: mvebu: Fix macro names and comments about legacy interrupts
dt-bindings: PCI: mvebu: Update information about intx interrupts
PCI: mvebu: Use child_ops API
PCI: mvebu: Add support for Advanced Error Reporting registers on emulated bridge
PCI: mvebu: Add support for PCI Bridge Subsystem Vendor ID on emulated bridge
PCI: mvebu: Correctly configure x1/x4 mode
dt-bindings: PCI: mvebu: Add num-lanes property
PCI: pci-bridge-emul: Add support for PCI Bridge Subsystem Vendor ID capability
PCI: pci-bridge-emul: Add support for PCIe extended capabilities
PCI: pci-bridge-emul: Re-arrange register tests
PCI: mvebu: Fix reporting Data Link Layer Link Active on emulated bridge
PCI: mvebu: Update comment for PCI_EXP_LNKCTL register on emulated bridge
PCI: mvebu: Update comment for PCI_EXP_LNKCAP register on emulated bridge
PCI: mvebu: Properly initialize vendor, device and revision of emulated bridge
PCI: mvebu: Set PCI_BRIDGE_EMUL_NO_IO_FORWARD when IO is unsupported
PCI: mvebu: Remove duplicate nports assignment
PCI: mvebu: Add help string for CONFIG_PCI_MVEBU option
PCI: pci-bridge-emul: Add support for new flag PCI_BRIDGE_EMUL_NO_IO_FORWARD
PCI: pci-bridge-emul: Rename PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR to PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD
PCI: pci-bridge-emul: Make struct pci_bridge_emul_ops as const
MAINTAINERS: Add Pali Rohár as pci-mvebu.c maintainer
Diffstat (limited to 'drivers/pci/pci-bridge-emul.c')
-rw-r--r-- | drivers/pci/pci-bridge-emul.c | 178 |
1 files changed, 122 insertions, 56 deletions
diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c index ec6ab03ae476..9c2ca28e3ecf 100644 --- a/drivers/pci/pci-bridge-emul.c +++ b/drivers/pci/pci-bridge-emul.c @@ -21,8 +21,11 @@ #include "pci-bridge-emul.h" #define PCI_BRIDGE_CONF_END PCI_STD_HEADER_SIZEOF +#define PCI_CAP_SSID_SIZEOF (PCI_SSVID_DEVICE_ID + 2) +#define PCI_CAP_SSID_START PCI_BRIDGE_CONF_END +#define PCI_CAP_SSID_END (PCI_CAP_SSID_START + PCI_CAP_SSID_SIZEOF) #define PCI_CAP_PCIE_SIZEOF (PCI_EXP_SLTSTA2 + 2) -#define PCI_CAP_PCIE_START PCI_BRIDGE_CONF_END +#define PCI_CAP_PCIE_START PCI_CAP_SSID_END #define PCI_CAP_PCIE_END (PCI_CAP_PCIE_START + PCI_CAP_PCIE_SIZEOF) /** @@ -315,6 +318,25 @@ struct pci_bridge_reg_behavior pcie_cap_regs_behavior[PCI_CAP_PCIE_SIZEOF / 4] = }, }; +static pci_bridge_emul_read_status_t +pci_bridge_emul_read_ssid(struct pci_bridge_emul *bridge, int reg, u32 *value) +{ + switch (reg) { + case PCI_CAP_LIST_ID: + *value = PCI_CAP_ID_SSVID | + (bridge->has_pcie ? (PCI_CAP_PCIE_START << 8) : 0); + return PCI_BRIDGE_EMUL_HANDLED; + + case PCI_SSVID_VENDOR_ID: + *value = bridge->subsystem_vendor_id | + (bridge->subsystem_id << 16); + return PCI_BRIDGE_EMUL_HANDLED; + + default: + return PCI_BRIDGE_EMUL_NOT_HANDLED; + } +} + /* * Initialize a pci_bridge_emul structure to represent a fake PCI * bridge configuration space. The caller needs to have initialized @@ -343,9 +365,17 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge, if (!bridge->pci_regs_behavior) return -ENOMEM; - if (bridge->has_pcie) { + if (bridge->subsystem_vendor_id) + bridge->conf.capabilities_pointer = PCI_CAP_SSID_START; + else if (bridge->has_pcie) bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START; + else + bridge->conf.capabilities_pointer = 0; + + if (bridge->conf.capabilities_pointer) bridge->conf.status |= cpu_to_le16(PCI_STATUS_CAP_LIST); + + if (bridge->has_pcie) { bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP; bridge->pcie_conf.cap |= cpu_to_le16(PCI_EXP_TYPE_ROOT_PORT << 4); bridge->pcie_cap_regs_behavior = @@ -379,11 +409,20 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge, ~(BIT(10) << 16); } - if (flags & PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR) { + if (flags & PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD) { bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].ro = ~0; bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].rw = 0; } + if (flags & PCI_BRIDGE_EMUL_NO_IO_FORWARD) { + bridge->pci_regs_behavior[PCI_COMMAND / 4].ro |= PCI_COMMAND_IO; + bridge->pci_regs_behavior[PCI_COMMAND / 4].rw &= ~PCI_COMMAND_IO; + bridge->pci_regs_behavior[PCI_IO_BASE / 4].ro |= GENMASK(15, 0); + bridge->pci_regs_behavior[PCI_IO_BASE / 4].rw &= ~GENMASK(15, 0); + bridge->pci_regs_behavior[PCI_IO_BASE_UPPER16 / 4].ro = ~0; + bridge->pci_regs_behavior[PCI_IO_BASE_UPPER16 / 4].rw = 0; + } + return 0; } EXPORT_SYMBOL_GPL(pci_bridge_emul_init); @@ -415,25 +454,33 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, __le32 *cfgspace; const struct pci_bridge_reg_behavior *behavior; - if (bridge->has_pcie && reg >= PCI_CAP_PCIE_END) { - *value = 0; - return PCIBIOS_SUCCESSFUL; - } - - if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END) { - *value = 0; - return PCIBIOS_SUCCESSFUL; - } - - if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) { + if (reg < PCI_BRIDGE_CONF_END) { + /* Emulated PCI space */ + read_op = bridge->ops->read_base; + cfgspace = (__le32 *) &bridge->conf; + behavior = bridge->pci_regs_behavior; + } else if (reg >= PCI_CAP_SSID_START && reg < PCI_CAP_SSID_END && bridge->subsystem_vendor_id) { + /* Emulated PCI Bridge Subsystem Vendor ID capability */ + reg -= PCI_CAP_SSID_START; + read_op = pci_bridge_emul_read_ssid; + cfgspace = NULL; + behavior = NULL; + } else if (reg >= PCI_CAP_PCIE_START && reg < PCI_CAP_PCIE_END && bridge->has_pcie) { + /* Our emulated PCIe capability */ reg -= PCI_CAP_PCIE_START; read_op = bridge->ops->read_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; + } else if (reg >= PCI_CFG_SPACE_SIZE && bridge->has_pcie) { + /* PCIe extended capability space */ + reg -= PCI_CFG_SPACE_SIZE; + read_op = bridge->ops->read_ext; + cfgspace = NULL; + behavior = NULL; } else { - read_op = bridge->ops->read_base; - cfgspace = (__le32 *) &bridge->conf; - behavior = bridge->pci_regs_behavior; + /* Not implemented */ + *value = 0; + return PCIBIOS_SUCCESSFUL; } if (read_op) @@ -441,15 +488,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; @@ -477,11 +529,31 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, __le32 *cfgspace; const struct pci_bridge_reg_behavior *behavior; - if (bridge->has_pcie && reg >= PCI_CAP_PCIE_END) - return PCIBIOS_SUCCESSFUL; + ret = pci_bridge_emul_conf_read(bridge, reg, 4, &old); + if (ret != PCIBIOS_SUCCESSFUL) + return ret; - if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END) + if (reg < PCI_BRIDGE_CONF_END) { + /* Emulated PCI space */ + write_op = bridge->ops->write_base; + cfgspace = (__le32 *) &bridge->conf; + behavior = bridge->pci_regs_behavior; + } else if (reg >= PCI_CAP_PCIE_START && reg < PCI_CAP_PCIE_END && bridge->has_pcie) { + /* Our emulated PCIe capability */ + reg -= PCI_CAP_PCIE_START; + write_op = bridge->ops->write_pcie; + cfgspace = (__le32 *) &bridge->pcie_conf; + behavior = bridge->pcie_cap_regs_behavior; + } else if (reg >= PCI_CFG_SPACE_SIZE && bridge->has_pcie) { + /* PCIe extended capability space */ + reg -= PCI_CFG_SPACE_SIZE; + write_op = bridge->ops->write_ext; + cfgspace = NULL; + behavior = NULL; + } else { + /* Not implemented */ return PCIBIOS_SUCCESSFUL; + } shift = (where & 0x3) * 8; @@ -494,44 +566,38 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, else return PCIBIOS_BAD_REGISTER_NUMBER; - ret = pci_bridge_emul_conf_read(bridge, reg, 4, &old); - if (ret != PCIBIOS_SUCCESSFUL) - return ret; + if (behavior) { + /* Keep all bits, except the RW bits */ + new = old & (~mask | ~behavior[reg / 4].rw); - if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) { - reg -= PCI_CAP_PCIE_START; - write_op = bridge->ops->write_pcie; - cfgspace = (__le32 *) &bridge->pcie_conf; - behavior = bridge->pcie_cap_regs_behavior; + /* 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)); } else { - write_op = bridge->ops->write_base; - cfgspace = (__le32 *) &bridge->conf; - behavior = bridge->pci_regs_behavior; + new = old & ~mask; + new |= (value << shift) & mask; } - /* 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); - - /* Clear the W1C bits */ - new &= ~((value << shift) & (behavior[reg / 4].w1c & mask)); - - /* Save the new value with the cleared W1C bits into the cfgspace */ - cfgspace[reg / 4] = cpu_to_le32(new); + if (cfgspace) { + /* Save the new value with the cleared W1C bits into the cfgspace */ + cfgspace[reg / 4] = cpu_to_le32(new); + } - /* - * Clear the W1C bits not specified by the write mask, so that the - * write_op() does not clear them. - */ - new &= ~(behavior[reg / 4].w1c & ~mask); + if (behavior) { + /* + * Clear the W1C bits not specified by the write mask, so that the + * write_op() does not clear them. + */ + new &= ~(behavior[reg / 4].w1c & ~mask); - /* - * Set the W1C bits specified by the write mask, so that write_op() - * knows about that they are to be cleared. - */ - new |= (value << shift) & (behavior[reg / 4].w1c & mask); + /* + * Set the W1C bits specified by the write mask, so that write_op() + * knows about that they are to be cleared. + */ + new |= (value << shift) & (behavior[reg / 4].w1c & mask); + } if (write_op) write_op(bridge, reg, old, new, mask); |