255 lines
8.1 KiB
Diff
255 lines
8.1 KiB
Diff
From ca6a33ed3efd3a3d569744290860d0d827ca4196 Mon Sep 17 00:00:00 2001
|
|
From: Russell King <rmk+kernel@armlinux.org.uk>
|
|
Date: Tue, 2 Feb 2021 13:45:28 +0000
|
|
Subject: PCI: pci-bridge-emul: re-arrange register tests
|
|
|
|
Re-arrange the tests for which sets of registers are being accessed
|
|
so that it is easier to add further regions later. No functional
|
|
change.
|
|
|
|
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
|
|
---
|
|
drivers/pci/pci-bridge-emul.c | 53 ++++++++++++++++++++++---------------------
|
|
1 file changed, 27 insertions(+), 26 deletions(-)
|
|
|
|
diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c
|
|
index fdaf86a888b7..9988078e7b0e 100644
|
|
--- a/drivers/pci/pci-bridge-emul.c
|
|
+++ b/drivers/pci/pci-bridge-emul.c
|
|
@@ -328,25 +328,25 @@ 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_BRIDGE_CONF_END) {
|
|
+ /* Emulated PCI space */
|
|
+ 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;
|
|
- }
|
|
-
|
|
- if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) {
|
|
+ } else if (reg < PCI_CAP_PCIE_END) {
|
|
+ /* 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 {
|
|
- read_op = bridge->ops->read_base;
|
|
- cfgspace = (__le32 *) &bridge->conf;
|
|
- behavior = bridge->pci_regs_behavior;
|
|
+ /* Beyond our PCIe space */
|
|
+ *value = 0;
|
|
+ return PCIBIOS_SUCCESSFUL;
|
|
}
|
|
|
|
if (read_op)
|
|
@@ -390,11 +390,23 @@ 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)
|
|
+ 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 (!bridge->has_pcie) {
|
|
+ /* PCIe space is not implemented, and no PCI capabilities */
|
|
return PCIBIOS_SUCCESSFUL;
|
|
-
|
|
- if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END)
|
|
+ } else if (reg < PCI_CAP_PCIE_END) {
|
|
+ /* 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 {
|
|
return PCIBIOS_SUCCESSFUL;
|
|
+ }
|
|
|
|
shift = (where & 0x3) * 8;
|
|
|
|
@@ -411,17 +423,6 @@ 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) {
|
|
- reg -= PCI_CAP_PCIE_START;
|
|
- write_op = bridge->ops->write_pcie;
|
|
- cfgspace = (__le32 *) &bridge->pcie_conf;
|
|
- behavior = bridge->pcie_cap_regs_behavior;
|
|
- } else {
|
|
- write_op = bridge->ops->write_base;
|
|
- cfgspace = (__le32 *) &bridge->conf;
|
|
- behavior = bridge->pci_regs_behavior;
|
|
- }
|
|
-
|
|
/* Keep all bits, except the RW bits */
|
|
new = old & (~mask | ~behavior[reg / 4].rw);
|
|
|
|
--
|
|
cgit v1.2.3
|
|
From 8bc25ed1e6eb38000f91e1eb71bc097012f5dcb1 Mon Sep 17 00:00:00 2001
|
|
From: Russell King <rmk+kernel@armlinux.org.uk>
|
|
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 <rmk+kernel@armlinux.org.uk>
|
|
---
|
|
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;
|
|
@@ -443,17 +443,24 @@ 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);
|
|
-
|
|
- /* 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 (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);
|
|
+
|
|
+ /* Clear the W1C bits */
|
|
+ new &= ~((value << shift) & (behavior[reg / 4].w1c & mask));
|
|
+ } else {
|
|
+ new = old & ~mask;
|
|
+ new |= (value << shift) & mask;
|
|
+ }
|
|
+
|
|
+ /* Save the new value with the cleared W1C bits into the cfgspace */
|
|
+ if (cfgspace)
|
|
+ cfgspace[reg / 4] = cpu_to_le32(new);
|
|
|
|
/*
|
|
* Clear the W1C bits not specified by the write mask, so that the
|
|
diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h
|
|
index 49bbd37ee318..2552ab660b08 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 v1.2.3
|
|
|