1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
From 09ccac06bf207e939bd00487bb8e2b7bdc1cddf2 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: [PATCH 25/90] PCI: pci-bridge-emul: Add support for PCIe extended
capabilities
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add support for PCIe extended capabilities, which we just redirect to the
emulating driver.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
[pali: Fix writing new value with W1C bits]
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Marek Behún <kabel@kernel.org>
---
drivers/pci/pci-bridge-emul.c | 77 +++++++++++++++++++++++------------
drivers/pci/pci-bridge-emul.h | 15 +++++++
2 files changed, 67 insertions(+), 25 deletions(-)
diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c
index a956408834d6..c4b9837006ff 100644
--- a/drivers/pci/pci-bridge-emul.c
+++ b/drivers/pci/pci-bridge-emul.c
@@ -437,10 +437,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)
@@ -448,15 +454,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;
@@ -502,8 +513,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;
@@ -517,29 +535,38 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
else
return PCIBIOS_BAD_REGISTER_NUMBER;
- /* 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;
+ }
- /* 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);
diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h
index 4953274cac18..6b5f75b2ad02 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;
--
2.34.1
|