PCI: pci-bridge-emul: Add support for PCI Bridge Subsystem Vendor ID capability
This is read-only capability in PCI config space. Put it between base PCI capability and base PCI Express capability. Driver just have to specify subsystem_vendor_id and subsystem_id fields in emulated bridge structure and pci-bridge-emul takes care of correctly compose PCI Bridge Subsystem Vendor ID capability. Link: https://lore.kernel.org/r/20220222155030.988-4-pali@kernel.org Signed-off-by: Pali Rohár <pali@kernel.org> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
This commit is contained in:
parent
c0bd419732
commit
3767a90242
|
@ -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
|
||||
|
@ -341,9 +363,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 =
|
||||
|
@ -427,26 +457,28 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
|
|||
read_op = bridge->ops->read_base;
|
||||
cfgspace = (__le32 *) &bridge->conf;
|
||||
behavior = bridge->pci_regs_behavior;
|
||||
} else if (!bridge->has_pcie) {
|
||||
/* PCIe space is not implemented, and no PCI capabilities */
|
||||
*value = 0;
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
} else if (reg < PCI_CAP_PCIE_END) {
|
||||
} 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) {
|
||||
/* Rest of PCI space not implemented */
|
||||
*value = 0;
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
} else {
|
||||
} 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 {
|
||||
/* Not implemented */
|
||||
*value = 0;
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
if (read_op)
|
||||
|
@ -504,24 +536,21 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
|
|||
write_op = bridge->ops->write_base;
|
||||
cfgspace = (__le32 *) &bridge->conf;
|
||||
behavior = bridge->pci_regs_behavior;
|
||||
} else if (!bridge->has_pcie) {
|
||||
/* PCIe space is not implemented, and no PCI capabilities */
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
} else if (reg < PCI_CAP_PCIE_END) {
|
||||
} 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) {
|
||||
/* Rest of PCI space not implemented */
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
} else {
|
||||
} 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;
|
||||
|
|
|
@ -132,6 +132,8 @@ struct pci_bridge_emul {
|
|||
struct pci_bridge_reg_behavior *pcie_cap_regs_behavior;
|
||||
void *data;
|
||||
bool has_pcie;
|
||||
u16 subsystem_vendor_id;
|
||||
u16 subsystem_id;
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
Loading…
Reference in New Issue