diff options
author | Thomas Petazzoni <thomas.petazzoni@bootlin.com> | 2018-10-18 11:37:16 -0400 |
---|---|---|
committer | Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | 2018-10-18 12:50:19 -0400 |
commit | 23a5fba4d9411787c8e86ff5808e7d8e41bf3935 (patch) | |
tree | 2569b40cb46199c00331338936d203dffbfbfbae | |
parent | 684e07ed39ddd12731eda9933a946c7424e91c14 (diff) |
PCI: Introduce PCI bridge emulated config space common logic
Some PCI host controllers do not expose a configuration space for the
root port PCI bridge. Due to this, the Marvell Armada 370/38x/XP PCI
controller driver (pci-mvebu) emulates a root port PCI bridge
configuration space, and uses that to (among other things) dynamically
create the memory windows that correspond to the PCI MEM and I/O
regions.
Since we now need to add a very similar logic for the Marvell Armada
37xx PCI controller driver (pci-aardvark), instead of duplicating the
code, we create in this commit a common logic called pci-bridge-emul.
The idea of this logic is to emulate a root port PCI bridge
configuration space by providing configuration space read/write
operations, and faking behind the scenes the configuration space of a
PCI bridge. A PCI host controller driver simply has to call
pci_bridge_emul_conf_read() and pci_bridge_emul_conf_write() to
read/write the configuration space of the bridge.
By default, the PCI bridge configuration space is simply emulated by a
chunk of memory, but the PCI host controller can override the behavior
of the read and write operations on a per-register basis to do
additional actions if needed. We take care of complying with the
behavior of the PCI configuration space registers in terms of bits
that are read-write, read-only, reserved and write-1-to-clear.
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Russell King <rmk+kernel@armlinux.org.uk>
-rw-r--r-- | drivers/pci/Kconfig | 3 | ||||
-rw-r--r-- | drivers/pci/Makefile | 1 | ||||
-rw-r--r-- | drivers/pci/pci-bridge-emul.c | 408 | ||||
-rw-r--r-- | drivers/pci/pci-bridge-emul.h | 124 |
4 files changed, 536 insertions, 0 deletions
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 56ff8f6d31fc..4a28e07d4c0e 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig | |||
@@ -98,6 +98,9 @@ config PCI_ECAM | |||
98 | config PCI_LOCKLESS_CONFIG | 98 | config PCI_LOCKLESS_CONFIG |
99 | bool | 99 | bool |
100 | 100 | ||
101 | config PCI_BRIDGE_EMUL | ||
102 | bool | ||
103 | |||
101 | config PCI_IOV | 104 | config PCI_IOV |
102 | bool "PCI IOV support" | 105 | bool "PCI IOV support" |
103 | depends on PCI | 106 | depends on PCI |
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 1b2cfe51e8d7..3e5caf7347e9 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile | |||
@@ -19,6 +19,7 @@ obj-$(CONFIG_HOTPLUG_PCI) += hotplug/ | |||
19 | obj-$(CONFIG_PCI_MSI) += msi.o | 19 | obj-$(CONFIG_PCI_MSI) += msi.o |
20 | obj-$(CONFIG_PCI_ATS) += ats.o | 20 | obj-$(CONFIG_PCI_ATS) += ats.o |
21 | obj-$(CONFIG_PCI_IOV) += iov.o | 21 | obj-$(CONFIG_PCI_IOV) += iov.o |
22 | obj-$(CONFIG_PCI_BRIDGE_EMUL) += pci-bridge-emul.o | ||
22 | obj-$(CONFIG_ACPI) += pci-acpi.o | 23 | obj-$(CONFIG_ACPI) += pci-acpi.o |
23 | obj-$(CONFIG_PCI_LABEL) += pci-label.o | 24 | obj-$(CONFIG_PCI_LABEL) += pci-label.o |
24 | obj-$(CONFIG_X86_INTEL_MID) += pci-mid.o | 25 | obj-$(CONFIG_X86_INTEL_MID) += pci-mid.o |
diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c new file mode 100644 index 000000000000..129738362d90 --- /dev/null +++ b/drivers/pci/pci-bridge-emul.c | |||
@@ -0,0 +1,408 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (C) 2018 Marvell | ||
4 | * | ||
5 | * Author: Thomas Petazzoni <thomas.petazzoni@bootlin.com> | ||
6 | * | ||
7 | * This file helps PCI controller drivers implement a fake root port | ||
8 | * PCI bridge when the HW doesn't provide such a root port PCI | ||
9 | * bridge. | ||
10 | * | ||
11 | * It emulates a PCI bridge by providing a fake PCI configuration | ||
12 | * space (and optionally a PCIe capability configuration space) in | ||
13 | * memory. By default the read/write operations simply read and update | ||
14 | * this fake configuration space in memory. However, PCI controller | ||
15 | * drivers can provide through the 'struct pci_sw_bridge_ops' | ||
16 | * structure a set of operations to override or complement this | ||
17 | * default behavior. | ||
18 | */ | ||
19 | |||
20 | #include <linux/pci.h> | ||
21 | #include "pci-bridge-emul.h" | ||
22 | |||
23 | #define PCI_BRIDGE_CONF_END PCI_STD_HEADER_SIZEOF | ||
24 | #define PCI_CAP_PCIE_START PCI_BRIDGE_CONF_END | ||
25 | #define PCI_CAP_PCIE_END (PCI_CAP_PCIE_START + PCI_EXP_SLTSTA2 + 2) | ||
26 | |||
27 | /* | ||
28 | * Initialize a pci_bridge_emul structure to represent a fake PCI | ||
29 | * bridge configuration space. The caller needs to have initialized | ||
30 | * the PCI configuration space with whatever values make sense | ||
31 | * (typically at least vendor, device, revision), the ->ops pointer, | ||
32 | * and optionally ->data and ->has_pcie. | ||
33 | */ | ||
34 | void pci_bridge_emul_init(struct pci_bridge_emul *bridge) | ||
35 | { | ||
36 | bridge->conf.class_revision |= PCI_CLASS_BRIDGE_PCI << 16; | ||
37 | bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE; | ||
38 | bridge->conf.cache_line_size = 0x10; | ||
39 | bridge->conf.status = PCI_STATUS_CAP_LIST; | ||
40 | |||
41 | if (bridge->has_pcie) { | ||
42 | bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START; | ||
43 | bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP; | ||
44 | /* Set PCIe v2, root port, slot support */ | ||
45 | bridge->pcie_conf.cap = PCI_EXP_TYPE_ROOT_PORT << 4 | 2 | | ||
46 | PCI_EXP_FLAGS_SLOT; | ||
47 | } | ||
48 | } | ||
49 | |||
50 | struct pci_bridge_reg_behavior { | ||
51 | /* Read-only bits */ | ||
52 | u32 ro; | ||
53 | |||
54 | /* Read-write bits */ | ||
55 | u32 rw; | ||
56 | |||
57 | /* Write-1-to-clear bits */ | ||
58 | u32 w1c; | ||
59 | |||
60 | /* Reserved bits (hardwired to 0) */ | ||
61 | u32 rsvd; | ||
62 | }; | ||
63 | |||
64 | const static struct pci_bridge_reg_behavior pci_regs_behavior[] = { | ||
65 | [PCI_VENDOR_ID / 4] = { .ro = ~0 }, | ||
66 | [PCI_COMMAND / 4] = { | ||
67 | .rw = (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | | ||
68 | PCI_COMMAND_MASTER | PCI_COMMAND_PARITY | | ||
69 | PCI_COMMAND_SERR), | ||
70 | .ro = ((PCI_COMMAND_SPECIAL | PCI_COMMAND_INVALIDATE | | ||
71 | PCI_COMMAND_VGA_PALETTE | PCI_COMMAND_WAIT | | ||
72 | PCI_COMMAND_FAST_BACK) | | ||
73 | (PCI_STATUS_CAP_LIST | PCI_STATUS_66MHZ | | ||
74 | PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MASK) << 16), | ||
75 | .rsvd = GENMASK(15, 10) | ((BIT(6) | GENMASK(3, 0)) << 16), | ||
76 | .w1c = (PCI_STATUS_PARITY | | ||
77 | PCI_STATUS_SIG_TARGET_ABORT | | ||
78 | PCI_STATUS_REC_TARGET_ABORT | | ||
79 | PCI_STATUS_REC_MASTER_ABORT | | ||
80 | PCI_STATUS_SIG_SYSTEM_ERROR | | ||
81 | PCI_STATUS_DETECTED_PARITY) << 16, | ||
82 | }, | ||
83 | [PCI_CLASS_REVISION / 4] = { .ro = ~0 }, | ||
84 | |||
85 | /* | ||
86 | * Cache Line Size register: implement as read-only, we do not | ||
87 | * pretend implementing "Memory Write and Invalidate" | ||
88 | * transactions" | ||
89 | * | ||
90 | * Latency Timer Register: implemented as read-only, as "A | ||
91 | * bridge that is not capable of a burst transfer of more than | ||
92 | * two data phases on its primary interface is permitted to | ||
93 | * hardwire the Latency Timer to a value of 16 or less" | ||
94 | * | ||
95 | * Header Type: always read-only | ||
96 | * | ||
97 | * BIST register: implemented as read-only, as "A bridge that | ||
98 | * does not support BIST must implement this register as a | ||
99 | * read-only register that returns 0 when read" | ||
100 | */ | ||
101 | [PCI_CACHE_LINE_SIZE / 4] = { .ro = ~0 }, | ||
102 | |||
103 | /* | ||
104 | * Base Address registers not used must be implemented as | ||
105 | * read-only registers that return 0 when read. | ||
106 | */ | ||
107 | [PCI_BASE_ADDRESS_0 / 4] = { .ro = ~0 }, | ||
108 | [PCI_BASE_ADDRESS_1 / 4] = { .ro = ~0 }, | ||
109 | |||
110 | [PCI_PRIMARY_BUS / 4] = { | ||
111 | /* Primary, secondary and subordinate bus are RW */ | ||
112 | .rw = GENMASK(24, 0), | ||
113 | /* Secondary latency is read-only */ | ||
114 | .ro = GENMASK(31, 24), | ||
115 | }, | ||
116 | |||
117 | [PCI_IO_BASE / 4] = { | ||
118 | /* The high four bits of I/O base/limit are RW */ | ||
119 | .rw = (GENMASK(15, 12) | GENMASK(7, 4)), | ||
120 | |||
121 | /* The low four bits of I/O base/limit are RO */ | ||
122 | .ro = (((PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK | | ||
123 | PCI_STATUS_DEVSEL_MASK) << 16) | | ||
124 | GENMASK(11, 8) | GENMASK(3, 0)), | ||
125 | |||
126 | .w1c = (PCI_STATUS_PARITY | | ||
127 | PCI_STATUS_SIG_TARGET_ABORT | | ||
128 | PCI_STATUS_REC_TARGET_ABORT | | ||
129 | PCI_STATUS_REC_MASTER_ABORT | | ||
130 | PCI_STATUS_SIG_SYSTEM_ERROR | | ||
131 | PCI_STATUS_DETECTED_PARITY) << 16, | ||
132 | |||
133 | .rsvd = ((BIT(6) | GENMASK(4, 0)) << 16), | ||
134 | }, | ||
135 | |||
136 | [PCI_MEMORY_BASE / 4] = { | ||
137 | /* The high 12-bits of mem base/limit are RW */ | ||
138 | .rw = GENMASK(31, 20) | GENMASK(15, 4), | ||
139 | |||
140 | /* The low four bits of mem base/limit are RO */ | ||
141 | .ro = GENMASK(19, 16) | GENMASK(3, 0), | ||
142 | }, | ||
143 | |||
144 | [PCI_PREF_MEMORY_BASE / 4] = { | ||
145 | /* The high 12-bits of pref mem base/limit are RW */ | ||
146 | .rw = GENMASK(31, 20) | GENMASK(15, 4), | ||
147 | |||
148 | /* The low four bits of pref mem base/limit are RO */ | ||
149 | .ro = GENMASK(19, 16) | GENMASK(3, 0), | ||
150 | }, | ||
151 | |||
152 | [PCI_PREF_BASE_UPPER32 / 4] = { | ||
153 | .rw = ~0, | ||
154 | }, | ||
155 | |||
156 | [PCI_PREF_LIMIT_UPPER32 / 4] = { | ||
157 | .rw = ~0, | ||
158 | }, | ||
159 | |||
160 | [PCI_IO_BASE_UPPER16 / 4] = { | ||
161 | .rw = ~0, | ||
162 | }, | ||
163 | |||
164 | [PCI_CAPABILITY_LIST / 4] = { | ||
165 | .ro = GENMASK(7, 0), | ||
166 | .rsvd = GENMASK(31, 8), | ||
167 | }, | ||
168 | |||
169 | [PCI_ROM_ADDRESS1 / 4] = { | ||
170 | .rw = GENMASK(31, 11) | BIT(0), | ||
171 | .rsvd = GENMASK(10, 1), | ||
172 | }, | ||
173 | |||
174 | /* | ||
175 | * Interrupt line (bits 7:0) are RW, interrupt pin (bits 15:8) | ||
176 | * are RO, and bridge control (31:16) are a mix of RW, RO, | ||
177 | * reserved and W1C bits | ||
178 | */ | ||
179 | [PCI_INTERRUPT_LINE / 4] = { | ||
180 | /* Interrupt line is RW */ | ||
181 | .rw = (GENMASK(7, 0) | | ||
182 | ((PCI_BRIDGE_CTL_PARITY | | ||
183 | PCI_BRIDGE_CTL_SERR | | ||
184 | PCI_BRIDGE_CTL_ISA | | ||
185 | PCI_BRIDGE_CTL_VGA | | ||
186 | PCI_BRIDGE_CTL_MASTER_ABORT | | ||
187 | PCI_BRIDGE_CTL_BUS_RESET | | ||
188 | BIT(8) | BIT(9) | BIT(11)) << 16)), | ||
189 | |||
190 | /* Interrupt pin is RO */ | ||
191 | .ro = (GENMASK(15, 8) | ((PCI_BRIDGE_CTL_FAST_BACK) << 16)), | ||
192 | |||
193 | .w1c = BIT(10) << 16, | ||
194 | |||
195 | .rsvd = (GENMASK(15, 12) | BIT(4)) << 16, | ||
196 | }, | ||
197 | }; | ||
198 | |||
199 | const static struct pci_bridge_reg_behavior pcie_cap_regs_behavior[] = { | ||
200 | [PCI_CAP_LIST_ID / 4] = { | ||
201 | /* | ||
202 | * Capability ID, Next Capability Pointer and | ||
203 | * Capabilities register are all read-only. | ||
204 | */ | ||
205 | .ro = ~0, | ||
206 | }, | ||
207 | |||
208 | [PCI_EXP_DEVCAP / 4] = { | ||
209 | .ro = ~0, | ||
210 | }, | ||
211 | |||
212 | [PCI_EXP_DEVCTL / 4] = { | ||
213 | /* Device control register is RW */ | ||
214 | .rw = GENMASK(15, 0), | ||
215 | |||
216 | /* | ||
217 | * Device status register has 4 bits W1C, then 2 bits | ||
218 | * RO, the rest is reserved | ||
219 | */ | ||
220 | .w1c = GENMASK(19, 16), | ||
221 | .ro = GENMASK(20, 19), | ||
222 | .rsvd = GENMASK(31, 21), | ||
223 | }, | ||
224 | |||
225 | [PCI_EXP_LNKCAP / 4] = { | ||
226 | /* All bits are RO, except bit 23 which is reserved */ | ||
227 | .ro = lower_32_bits(~BIT(23)), | ||
228 | .rsvd = BIT(23), | ||
229 | }, | ||
230 | |||
231 | [PCI_EXP_LNKCTL / 4] = { | ||
232 | /* | ||
233 | * Link control has bits [1:0] and [11:3] RW, the | ||
234 | * other bits are reserved. | ||
235 | * Link status has bits [13:0] RO, and bits [14:15] | ||
236 | * W1C. | ||
237 | */ | ||
238 | .rw = GENMASK(11, 3) | GENMASK(1, 0), | ||
239 | .ro = GENMASK(13, 0) << 16, | ||
240 | .w1c = GENMASK(15, 14) << 16, | ||
241 | .rsvd = GENMASK(15, 12) | BIT(2), | ||
242 | }, | ||
243 | |||
244 | [PCI_EXP_SLTCAP / 4] = { | ||
245 | .ro = ~0, | ||
246 | }, | ||
247 | |||
248 | [PCI_EXP_SLTCTL / 4] = { | ||
249 | /* | ||
250 | * Slot control has bits [12:0] RW, the rest is | ||
251 | * reserved. | ||
252 | * | ||
253 | * Slot status has a mix of W1C and RO bits, as well | ||
254 | * as reserved bits. | ||
255 | */ | ||
256 | .rw = GENMASK(12, 0), | ||
257 | .w1c = (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | | ||
258 | PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | | ||
259 | PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC) << 16, | ||
260 | .ro = (PCI_EXP_SLTSTA_MRLSS | PCI_EXP_SLTSTA_PDS | | ||
261 | PCI_EXP_SLTSTA_EIS) << 16, | ||
262 | .rsvd = GENMASK(15, 12) | (GENMASK(15, 9) << 16), | ||
263 | }, | ||
264 | |||
265 | [PCI_EXP_RTCTL / 4] = { | ||
266 | /* | ||
267 | * Root control has bits [4:0] RW, the rest is | ||
268 | * reserved. | ||
269 | * | ||
270 | * Root status has bit 0 RO, the rest is reserved. | ||
271 | */ | ||
272 | .rw = (PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE | | ||
273 | PCI_EXP_RTCTL_SEFEE | PCI_EXP_RTCTL_PMEIE | | ||
274 | PCI_EXP_RTCTL_CRSSVE), | ||
275 | .ro = PCI_EXP_RTCAP_CRSVIS << 16, | ||
276 | .rsvd = GENMASK(15, 5) | (GENMASK(15, 1) << 16), | ||
277 | }, | ||
278 | |||
279 | [PCI_EXP_RTSTA / 4] = { | ||
280 | .ro = GENMASK(15, 0) | PCI_EXP_RTSTA_PENDING, | ||
281 | .w1c = PCI_EXP_RTSTA_PME, | ||
282 | .rsvd = GENMASK(31, 18), | ||
283 | }, | ||
284 | }; | ||
285 | |||
286 | /* | ||
287 | * Should be called by the PCI controller driver when reading the PCI | ||
288 | * configuration space of the fake bridge. It will call back the | ||
289 | * ->ops->read_base or ->ops->read_pcie operations. | ||
290 | */ | ||
291 | int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, | ||
292 | int size, u32 *value) | ||
293 | { | ||
294 | int ret; | ||
295 | int reg = where & ~3; | ||
296 | pci_bridge_emul_read_status_t (*read_op)(struct pci_bridge_emul *bridge, | ||
297 | int reg, u32 *value); | ||
298 | u32 *cfgspace; | ||
299 | const struct pci_bridge_reg_behavior *behavior; | ||
300 | |||
301 | if (bridge->has_pcie && reg >= PCI_CAP_PCIE_END) { | ||
302 | *value = 0; | ||
303 | return PCIBIOS_SUCCESSFUL; | ||
304 | } | ||
305 | |||
306 | if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END) { | ||
307 | *value = 0; | ||
308 | return PCIBIOS_SUCCESSFUL; | ||
309 | } | ||
310 | |||
311 | if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) { | ||
312 | reg -= PCI_CAP_PCIE_START; | ||
313 | read_op = bridge->ops->read_pcie; | ||
314 | cfgspace = (u32 *) &bridge->pcie_conf; | ||
315 | behavior = pcie_cap_regs_behavior; | ||
316 | } else { | ||
317 | read_op = bridge->ops->read_base; | ||
318 | cfgspace = (u32 *) &bridge->conf; | ||
319 | behavior = pci_regs_behavior; | ||
320 | } | ||
321 | |||
322 | if (read_op) | ||
323 | ret = read_op(bridge, reg, value); | ||
324 | else | ||
325 | ret = PCI_BRIDGE_EMUL_NOT_HANDLED; | ||
326 | |||
327 | if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED) | ||
328 | *value = cfgspace[reg / 4]; | ||
329 | |||
330 | /* | ||
331 | * Make sure we never return any reserved bit with a value | ||
332 | * different from 0. | ||
333 | */ | ||
334 | *value &= ~behavior[reg / 4].rsvd; | ||
335 | |||
336 | if (size == 1) | ||
337 | *value = (*value >> (8 * (where & 3))) & 0xff; | ||
338 | else if (size == 2) | ||
339 | *value = (*value >> (8 * (where & 3))) & 0xffff; | ||
340 | else if (size != 4) | ||
341 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
342 | |||
343 | return PCIBIOS_SUCCESSFUL; | ||
344 | } | ||
345 | |||
346 | /* | ||
347 | * Should be called by the PCI controller driver when writing the PCI | ||
348 | * configuration space of the fake bridge. It will call back the | ||
349 | * ->ops->write_base or ->ops->write_pcie operations. | ||
350 | */ | ||
351 | int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, | ||
352 | int size, u32 value) | ||
353 | { | ||
354 | int reg = where & ~3; | ||
355 | int mask, ret, old, new, shift; | ||
356 | void (*write_op)(struct pci_bridge_emul *bridge, int reg, | ||
357 | u32 old, u32 new, u32 mask); | ||
358 | u32 *cfgspace; | ||
359 | const struct pci_bridge_reg_behavior *behavior; | ||
360 | |||
361 | if (bridge->has_pcie && reg >= PCI_CAP_PCIE_END) | ||
362 | return PCIBIOS_SUCCESSFUL; | ||
363 | |||
364 | if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END) | ||
365 | return PCIBIOS_SUCCESSFUL; | ||
366 | |||
367 | shift = (where & 0x3) * 8; | ||
368 | |||
369 | if (size == 4) | ||
370 | mask = 0xffffffff; | ||
371 | else if (size == 2) | ||
372 | mask = 0xffff << shift; | ||
373 | else if (size == 1) | ||
374 | mask = 0xff << shift; | ||
375 | else | ||
376 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
377 | |||
378 | ret = pci_bridge_emul_conf_read(bridge, reg, 4, &old); | ||
379 | if (ret != PCIBIOS_SUCCESSFUL) | ||
380 | return ret; | ||
381 | |||
382 | if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) { | ||
383 | reg -= PCI_CAP_PCIE_START; | ||
384 | write_op = bridge->ops->write_pcie; | ||
385 | cfgspace = (u32 *) &bridge->pcie_conf; | ||
386 | behavior = pcie_cap_regs_behavior; | ||
387 | } else { | ||
388 | write_op = bridge->ops->write_base; | ||
389 | cfgspace = (u32 *) &bridge->conf; | ||
390 | behavior = pci_regs_behavior; | ||
391 | } | ||
392 | |||
393 | /* Keep all bits, except the RW bits */ | ||
394 | new = old & (~mask | ~behavior[reg / 4].rw); | ||
395 | |||
396 | /* Update the value of the RW bits */ | ||
397 | new |= (value << shift) & (behavior[reg / 4].rw & mask); | ||
398 | |||
399 | /* Clear the W1C bits */ | ||
400 | new &= ~((value << shift) & (behavior[reg / 4].w1c & mask)); | ||
401 | |||
402 | cfgspace[reg / 4] = new; | ||
403 | |||
404 | if (write_op) | ||
405 | write_op(bridge, reg, old, new, mask); | ||
406 | |||
407 | return PCIBIOS_SUCCESSFUL; | ||
408 | } | ||
diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h new file mode 100644 index 000000000000..9d510ccf738b --- /dev/null +++ b/drivers/pci/pci-bridge-emul.h | |||
@@ -0,0 +1,124 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | #ifndef __PCI_BRIDGE_EMUL_H__ | ||
3 | #define __PCI_BRIDGE_EMUL_H__ | ||
4 | |||
5 | #include <linux/kernel.h> | ||
6 | |||
7 | /* PCI configuration space of a PCI-to-PCI bridge. */ | ||
8 | struct pci_bridge_emul_conf { | ||
9 | u16 vendor; | ||
10 | u16 device; | ||
11 | u16 command; | ||
12 | u16 status; | ||
13 | u32 class_revision; | ||
14 | u8 cache_line_size; | ||
15 | u8 latency_timer; | ||
16 | u8 header_type; | ||
17 | u8 bist; | ||
18 | u32 bar[2]; | ||
19 | u8 primary_bus; | ||
20 | u8 secondary_bus; | ||
21 | u8 subordinate_bus; | ||
22 | u8 secondary_latency_timer; | ||
23 | u8 iobase; | ||
24 | u8 iolimit; | ||
25 | u16 secondary_status; | ||
26 | u16 membase; | ||
27 | u16 memlimit; | ||
28 | u16 pref_mem_base; | ||
29 | u16 pref_mem_limit; | ||
30 | u32 prefbaseupper; | ||
31 | u32 preflimitupper; | ||
32 | u16 iobaseupper; | ||
33 | u16 iolimitupper; | ||
34 | u8 capabilities_pointer; | ||
35 | u8 reserve[3]; | ||
36 | u32 romaddr; | ||
37 | u8 intline; | ||
38 | u8 intpin; | ||
39 | u16 bridgectrl; | ||
40 | }; | ||
41 | |||
42 | /* PCI configuration space of the PCIe capabilities */ | ||
43 | struct pci_bridge_emul_pcie_conf { | ||
44 | u8 cap_id; | ||
45 | u8 next; | ||
46 | u16 cap; | ||
47 | u32 devcap; | ||
48 | u16 devctl; | ||
49 | u16 devsta; | ||
50 | u32 lnkcap; | ||
51 | u16 lnkctl; | ||
52 | u16 lnksta; | ||
53 | u32 slotcap; | ||
54 | u16 slotctl; | ||
55 | u16 slotsta; | ||
56 | u16 rootctl; | ||
57 | u16 rsvd; | ||
58 | u32 rootsta; | ||
59 | u32 devcap2; | ||
60 | u16 devctl2; | ||
61 | u16 devsta2; | ||
62 | u32 lnkcap2; | ||
63 | u16 lnkctl2; | ||
64 | u16 lnksta2; | ||
65 | u32 slotcap2; | ||
66 | u16 slotctl2; | ||
67 | u16 slotsta2; | ||
68 | }; | ||
69 | |||
70 | struct pci_bridge_emul; | ||
71 | |||
72 | typedef enum { PCI_BRIDGE_EMUL_HANDLED, | ||
73 | PCI_BRIDGE_EMUL_NOT_HANDLED } pci_bridge_emul_read_status_t; | ||
74 | |||
75 | struct pci_bridge_emul_ops { | ||
76 | /* | ||
77 | * Called when reading from the regular PCI bridge | ||
78 | * configuration space. Return PCI_BRIDGE_EMUL_HANDLED when the | ||
79 | * operation has handled the read operation and filled in the | ||
80 | * *value, or PCI_BRIDGE_EMUL_NOT_HANDLED when the read should | ||
81 | * be emulated by the common code by reading from the | ||
82 | * in-memory copy of the configuration space. | ||
83 | */ | ||
84 | pci_bridge_emul_read_status_t (*read_base)(struct pci_bridge_emul *bridge, | ||
85 | int reg, u32 *value); | ||
86 | |||
87 | /* | ||
88 | * Same as ->read_base(), except it is for reading from the | ||
89 | * PCIe capability configuration space. | ||
90 | */ | ||
91 | pci_bridge_emul_read_status_t (*read_pcie)(struct pci_bridge_emul *bridge, | ||
92 | int reg, u32 *value); | ||
93 | /* | ||
94 | * Called when writing to the regular PCI bridge configuration | ||
95 | * space. old is the current value, new is the new value being | ||
96 | * written, and mask indicates which parts of the value are | ||
97 | * being changed. | ||
98 | */ | ||
99 | void (*write_base)(struct pci_bridge_emul *bridge, int reg, | ||
100 | u32 old, u32 new, u32 mask); | ||
101 | |||
102 | /* | ||
103 | * Same as ->write_base(), except it is for writing from the | ||
104 | * PCIe capability configuration space. | ||
105 | */ | ||
106 | void (*write_pcie)(struct pci_bridge_emul *bridge, int reg, | ||
107 | u32 old, u32 new, u32 mask); | ||
108 | }; | ||
109 | |||
110 | struct pci_bridge_emul { | ||
111 | struct pci_bridge_emul_conf conf; | ||
112 | struct pci_bridge_emul_pcie_conf pcie_conf; | ||
113 | struct pci_bridge_emul_ops *ops; | ||
114 | void *data; | ||
115 | bool has_pcie; | ||
116 | }; | ||
117 | |||
118 | void pci_bridge_emul_init(struct pci_bridge_emul *bridge); | ||
119 | int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, | ||
120 | int size, u32 *value); | ||
121 | int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, | ||
122 | int size, u32 value); | ||
123 | |||
124 | #endif /* __PCI_BRIDGE_EMUL_H__ */ | ||