summaryrefslogtreecommitdiff
path: root/drivers/pci/pci-bridge-emul.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/pci-bridge-emul.c')
-rw-r--r--drivers/pci/pci-bridge-emul.c83
1 files changed, 50 insertions, 33 deletions
diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c
index ccf26d12ec61..c124b7ff1e22 100644
--- a/drivers/pci/pci-bridge-emul.c
+++ b/drivers/pci/pci-bridge-emul.c
@@ -151,6 +151,7 @@ static const struct pci_bridge_reg_behavior pci_regs_behavior[] = {
.rw = (GENMASK(7, 0) |
((PCI_BRIDGE_CTL_PARITY |
PCI_BRIDGE_CTL_SERR |
+ /* NOTE: PCIe does not allow ISA, VGA, MASTER_ABORT */
PCI_BRIDGE_CTL_ISA |
PCI_BRIDGE_CTL_VGA |
PCI_BRIDGE_CTL_MASTER_ABORT |
@@ -264,6 +265,7 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge,
bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE;
bridge->conf.cache_line_size = 0x10;
bridge->conf.status = cpu_to_le16(PCI_STATUS_CAP_LIST);
+ bridge->conf.bridgectrl = cpu_to_le16(PCI_BRIDGE_CTL_SERR);
bridge->pci_regs_behavior = kmemdup(pci_regs_behavior,
sizeof(pci_regs_behavior),
GFP_KERNEL);
@@ -321,25 +323,26 @@ 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) {
+ if (reg < PCI_CAP_PCIE_START) {
+ read_op = bridge->ops->read_base;
+ cfgspace = (__le32 *) &bridge->conf;
+ behavior = bridge->pci_regs_behavior;
+ } else if (!bridge->has_pcie) {
*value = 0;
return PCIBIOS_SUCCESSFUL;
- }
-
- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) {
+ } else if (reg < PCI_CAP_PCIE_END) {
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 < 0x100) {
+ *value = 0;
+ return PCIBIOS_SUCCESSFUL;
} else {
- read_op = bridge->ops->read_base;
- cfgspace = (__le32 *) &bridge->conf;
- behavior = bridge->pci_regs_behavior;
+ reg -= 0x100;
+ read_op = bridge->ops->read_ext;
+ cfgspace = NULL;
+ behavior = NULL;
}
if (read_op)
@@ -347,15 +350,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;
@@ -382,12 +390,6 @@ 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;
-
- if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END)
- return PCIBIOS_SUCCESSFUL;
-
shift = (where & 0x3) * 8;
if (size == 4)
@@ -403,27 +405,42 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
if (ret != PCIBIOS_SUCCESSFUL)
return ret;
- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) {
+ if (reg < PCI_CAP_PCIE_START) {
+ write_op = bridge->ops->write_base;
+ cfgspace = (__le32 *) &bridge->conf;
+ behavior = bridge->pci_regs_behavior;
+ } else if (!bridge->has_pcie) {
+ return PCIBIOS_SUCCESSFUL;
+ } else if (reg < PCI_CAP_PCIE_END) {
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 < 0x100) {
+ return PCIBIOS_SUCCESSFUL;
} else {
- write_op = bridge->ops->write_base;
- cfgspace = (__le32 *) &bridge->conf;
- behavior = bridge->pci_regs_behavior;
+ reg -= 0x100;
+ write_op = bridge->ops->write_ext;
+ cfgspace = NULL;
+ behavior = NULL;
}
- /* 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);