Hello,
The pci-mvebu driver already contains some logic to emulate a root
port PCI bridge. It turns out that we have a similar need for the
pci-aardvark driver. Instead of duplicating the same logic in two
drivers, this patch series starts by adding a small common
infrastructure that helps emulate a root port PCI bridge, converts
pci-mvebu to use it, and finally extends pci-aardvark to use it as
well.
Thanks to this, Marvell Armada 3720 based systems, which use the
Aarkvark PCI controller, will have better PCI support, by having a
root port PCI bridge exposed.
The emulated PCI bridge common logic is a proposal, I very much
welcome comments and suggestions. Also, if you feel that adding a
common logic for only two drivers is too early, I'm fine with
duplicating a bit of code betwen pci-mvebu and pci-aardvark.
Best regards,
Thomas
Thomas Petazzoni (2):
PCI: Introduce PCI software bridge common logic
PCI: mvebu: Convert to PCI software bridge
Zachary Zhang (1):
PCI: aardvark: Implement emulated root PCI bridge
drivers/pci/Kconfig | 3 +
drivers/pci/Makefile | 1 +
drivers/pci/controller/Kconfig | 2 +
drivers/pci/controller/pci-aardvark.c | 119 ++++++++++-
drivers/pci/controller/pci-mvebu.c | 370 ++++++++++------------------------
drivers/pci/pci-sw-bridge.c | 149 ++++++++++++++
include/linux/pci-sw-bridge.h | 125 ++++++++++++
7 files changed, 497 insertions(+), 272 deletions(-)
create mode 100644 drivers/pci/pci-sw-bridge.c
create mode 100644 include/linux/pci-sw-bridge.h
--
2.14.4
Some PCI host controllers do not expose at the hardware level a root
port PCI bridge. Due to this, the Marvell Armada 370/38x/XP PCI
controller driver (pci-mvebu) emulates a root port PCI bridge, 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-sw-bridge.
The idea of this logic is to emulate a root port PCI bridge 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_sw_bridge_read() and
pci_sw_bridge_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.
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
drivers/pci/Kconfig | 3 +
drivers/pci/Makefile | 1 +
drivers/pci/pci-sw-bridge.c | 149 ++++++++++++++++++++++++++++++++++++++++++
include/linux/pci-sw-bridge.h | 125 +++++++++++++++++++++++++++++++++++
4 files changed, 278 insertions(+)
create mode 100644 drivers/pci/pci-sw-bridge.c
create mode 100644 include/linux/pci-sw-bridge.h
@@ -0,0 +1,125 @@+#ifndef __PCI_SW_BRIDGE_H__+#define __PCI_SW_BRIDGE_H__++#include<linux/kernel.h>++/* PCI configuration space of a PCI-to-PCI bridge. */+structpci_sw_bridge_conf{+u16vendor;+u16device;+u16command;+u16status;+u8revision;+u8interface;+u16class;+u8cache_line_size;+u8latency_timer;+u8header_type;+u8bist;+u32bar[2];+u8primary_bus;+u8secondary_bus;+u8subordinate_bus;+u8secondary_latency_timer;+u8iobase;+u8iolimit;+u16secondary_status;+u16membase;+u16memlimit;+u16pref_mem_base;+u16pref_mem_limit;+u32prefbaseupper;+u32preflimitupper;+u16iobaseupper;+u16iolimitupper;+u8capabilities_pointer;+u8reserve[3];+u32romaddr;+u8intline;+u8intpin;+u16bridgectrl;+};++/* PCI configuration space of the PCIe capabilities */+structpci_sw_bridge_pcie_conf{+u8cap_id;+u8next;+u16cap;+u32devcap;+u16devctl;+u16devsta;+u32lnkcap;+u16lnkctl;+u16lnksta;+u32slotcap;+u16slotctl;+u16slotsta;+u16rootctl;+u16rsvd;+u32rootsta;+u32devcap2;+u16devctl2;+u16devsta2;+u32lnkcap2;+u16lnkctl2;+u16lnksta2;+u32slotcap2;+u16slotctl2;+u16slotsta2;+};++structpci_sw_bridge;++typedefenum{PCI_SW_BRIDGE_HANDLED,+PCI_SW_BRIDGE_NOT_HANDLED}pci_sw_bridge_read_status_t;++structpci_sw_bridge_ops{+/*+*CalledwhenreadingfromtheregularPCIbridge+*configurationspace.ReturnPCI_SW_BRIDGE_HANDLEDwhenthe+*operationhashandledthereadoperationandfilledinthe+**value,orPCI_SW_BRIDGE_NOT_HANDLEDwhenthereadshould+*beemulatedbythecommoncodebyreadingfromthe+*in-memorycopyoftheconfigurationspace.+*/+pci_sw_bridge_read_status_t(*read_base)(structpci_sw_bridge*bridge,+intreg,u32*value);++/*+*Sameas->read_base(),exceptitisforreadingfromthe+*PCIecapabilityconfigurationspace.+*/+pci_sw_bridge_read_status_t(*read_pcie)(structpci_sw_bridge*bridge,+intreg,u32*value);+/*+*CalledwhenwritingtotheregularPCIbridgeconfiguration+*space.oldisthecurrentvalue,newisthenewvaluebeing+*written,andmaskindicateswhichpartsofthevalueare+*beingchanged.+*/+void(*write_base)(structpci_sw_bridge*bridge,intreg,+u32old,u32new,u32mask);++/*+*Sameas->read_base(),exceptitisforreadingfromthe+*PCIecapabilityconfigurationspace.+*/+void(*write_pcie)(structpci_sw_bridge*bridge,intreg,+u32old,u32new,u32mask);+};++structpci_sw_bridge{+structpci_sw_bridge_confconf;+structpci_sw_bridge_pcie_confpcie_conf;+structpci_sw_bridge_ops*ops;+void*data;+boolhas_pcie;+};++voidpci_sw_bridge_init(structpci_sw_bridge*bridge);+intpci_sw_bridge_read(structpci_sw_bridge*bridge,intwhere,+intsize,u32*value);+intpci_sw_bridge_write(structpci_sw_bridge*bridge,intwhere,+intsize,u32value);++#endif /* __PCI_SW_BRIDGE_H__ */
[+cc Ray since he's doing something similar for iProc,
Ley Foon & Simon for a possible Altera & R-Car bug]
On Fri, Jun 29, 2018 at 11:22:29AM +0200, Thomas Petazzoni wrote:
Some PCI host controllers do not expose at the hardware level a root
port PCI bridge. Due to this, the Marvell Armada 370/38x/XP PCI
controller driver (pci-mvebu) emulates a root port PCI bridge, 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-sw-bridge.
The idea of this logic is to emulate a root port PCI bridge 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_sw_bridge_read() and
pci_sw_bridge_write() to read/write the configuration space of the
bridge.
These systems must actually have a Root Port (there's obviously a Port
that originates the link), but it's not visible in a spec-compliant
way. So this is basically a wrapper that translates accesses to
standard config space into the vendor-specific registers.
Since there really *is* a hardware bridge but it just doesn't have the
interface we expect, "sw_bridge" doesn't feel like quite the right
name for it. Maybe something related to adapter/emulation/interface/...?
There are several drivers that do this, and it would be really cool to
have them all do it in a similar way. I found at least these:
hv_pcifront_read_config()
mvebu_pcie_rd_conf()
thunder_ecam_config_read()
thunder_pem_config_read()
xgene_pcie_config_read32()
iproc_pcie_config_read32()
rcar_pcie_read_conf()
I *really* like the fact that your accessors use the normal register
names, e.g., PCI_EXP_DEVCAP instead of PCISWCAP_EXP_DEVCAP. That's
huge for greppability.
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.
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
drivers/pci/Kconfig | 3 +
drivers/pci/Makefile | 1 +
drivers/pci/pci-sw-bridge.c | 149 ++++++++++++++++++++++++++++++++++++++++++
include/linux/pci-sw-bridge.h | 125 +++++++++++++++++++++++++++++++++++
Could this go in drivers/pci instead of include/linux? I'd prefer to
hide it inside the PCI core if that's possible.
+ * Should be called by the PCI controller driver when reading the PCI
+ * configuration space of the fake bridge. It will call back the
+ * ->ops->read_base or ->ops->read_pcie operations.
+ */
+int pci_sw_bridge_read(struct pci_sw_bridge *bridge, int where,
+ int size, u32 *value)
We don't really have a strong convention about the names of config
accessors: *_rd_conf(), *_read_config(), *config_read() are all used.
Maybe we could at least include "conf" to connect this with config
space as opposed to MMIO space.
+{
+ int ret;
+ int reg = where & ~3;
+
+ if (bridge->has_pcie && reg >= PCI_CAP_PCIE_END) {
+ *value = 0;
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END) {
+ *value = 0;
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) {
+ reg -= PCI_CAP_PCIE_START;
+
+ if (bridge->ops->read_pcie)
+ ret = bridge->ops->read_pcie(bridge, reg, value);
+ else
+ ret = PCI_SW_BRIDGE_NOT_HANDLED;
+
+ if (ret == PCI_SW_BRIDGE_NOT_HANDLED)
+ *value = *((u32*) &bridge->pcie_conf + reg / 4);
+ } else {
+ if (bridge->ops->read_base)
+ ret = bridge->ops->read_base(bridge, reg, value);
+ else
+ ret = PCI_SW_BRIDGE_NOT_HANDLED;
+
+ if (ret == PCI_SW_BRIDGE_NOT_HANDLED)
+ *value = *((u32*) &bridge->conf + reg / 4);
I'm not sure about this part, where the config space that's not
handled by the bridge's ops ends up just being RAM that's readable and
writable with no effect on the hardware. That seems a little
counter-intuitive. I think dropping writes and reading zeroes would
simplify my mental model.
This isn't directly related to your patches, but this is a common
pattern with some variations between drivers, which makes me wonder
whether there are bugs lurking. These use "where & 3" for size 2:
advk_pcie_rd_conf()
mtk_pcie_hw_rd_cfg()
pci_generic_config_read32()
But these use "where & 2":
_altera_pcie_cfg_read()
rcar_pcie_read_conf()
I *think* this means that unaligned 2-byte config reads on Altera and
R-Car will return incorrect data. In both cases, the actual read seen
by the hardware is a 32-bit read, but I think we extract the wrong 16
bits from that result.
I wonder if there's a way to use a common helper function to do this.
+ return PCIBIOS_SUCCESSFUL;
+}
+ /*
+ * Called when writing to the regular PCI bridge configuration
+ * space. old is the current value, new is the new value being
+ * written, and mask indicates which parts of the value are
+ * being changed.
+ */
+ void (*write_base)(struct pci_sw_bridge *bridge, int reg,
+ u32 old, u32 new, u32 mask);
+
+ /*
+ * Same as ->read_base(), except it is for reading from the
+ * PCIe capability configuration space.
I think you mean "->write_base(), except it is for writing to"
Hello Bjorn,
Thanks for your review!
On Thu, 12 Jul 2018 14:58:02 -0500, Bjorn Helgaas wrote:
quoted
The idea of this logic is to emulate a root port PCI bridge 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_sw_bridge_read() and
pci_sw_bridge_write() to read/write the configuration space of the
bridge.
These systems must actually have a Root Port (there's obviously a Port
that originates the link), but it's not visible in a spec-compliant
way. So this is basically a wrapper that translates accesses to
standard config space into the vendor-specific registers.
Correct.
Since there really *is* a hardware bridge but it just doesn't have the
interface we expect, "sw_bridge" doesn't feel like quite the right
name for it. Maybe something related to adapter/emulation/interface/...?
sw_adapter ? sw_adap ?
There are several drivers that do this, and it would be really cool to
have them all do it in a similar way. I found at least these:
hv_pcifront_read_config()
This one indeed looks potentially a bit similar.
mvebu_pcie_rd_conf()
This one is converted by my patch series.
thunder_ecam_config_read()
This one I'm not sure what is happening,
thunder_pem_config_read()
For this one, it seems like the HW exposes a config space, just that it
is bogus. It is a bit of a different situation. It could be handled by
the same sw_bridge/sw_adapt stuff, but it's a slightly different use
case than "the HW doesn't expose any spec-compliant root port config
space".
xgene_pcie_config_read32()
I don't see why this one would need sw_bridge/sw_adap.
iproc_pcie_config_read32()
Not sure about this one either.
rcar_pcie_read_conf()
Perhaps it needs sw_bridge/sw_adap.
Really for all those ones, it would require some involvement from the
corresponding driver maintainers, who understand better their HW and
see if they would benefit from sw_bridge/sw_adap.
I *really* like the fact that your accessors use the normal register
names, e.g., PCI_EXP_DEVCAP instead of PCISWCAP_EXP_DEVCAP. That's
huge for greppability.
Thanks. There's however one down-side: I had to add separate accessors
for reading/writing the main PCI config space and the PCIe capability
config space. Any additional capability config space would require
adding another pair of accessors. But I really wanted to re-use the
existing PCI_EXP_* definitions. Another approach is perhaps to pass an
"ID" of the config space being accessed, i.e 0x0 for the main config
space, and PCI_CAP_ID_EXP for PCI express.
So instead of 4 accessors in pci_sw_bridge_ops, we would have only two:
pci_sw_bridge_read_status_t (*read)(struct pci_sw_bridge *bridge,
int id, int reg, u32 *value);
void (*write)(struct pci_sw_bridge *bridge, int id, int reg,
u32 old, u32 new, u32 mask);
where "id" would allow the accessor to know if we're accessing the main
config space, or the PCI express capability config space.
How does that sound ?
quoted
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.
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
drivers/pci/Kconfig | 3 +
drivers/pci/Makefile | 1 +
drivers/pci/pci-sw-bridge.c | 149 ++++++++++++++++++++++++++++++++++++++++++
include/linux/pci-sw-bridge.h | 125 +++++++++++++++++++++++++++++++++++
Could this go in drivers/pci instead of include/linux? I'd prefer to
hide it inside the PCI core if that's possible.
Sure, I'll fix that.
quoted
+ * Should be called by the PCI controller driver when reading the PCI
+ * configuration space of the fake bridge. It will call back the
+ * ->ops->read_base or ->ops->read_pcie operations.
+ */
+int pci_sw_bridge_read(struct pci_sw_bridge *bridge, int where,
+ int size, u32 *value)
We don't really have a strong convention about the names of config
accessors: *_rd_conf(), *_read_config(), *config_read() are all used.
Maybe we could at least include "conf" to connect this with config
space as opposed to MMIO space.
No problem, I'll fix that, makes perfect sense.
quoted
+ if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) {
+ reg -= PCI_CAP_PCIE_START;
+
+ if (bridge->ops->read_pcie)
+ ret = bridge->ops->read_pcie(bridge, reg, value);
+ else
+ ret = PCI_SW_BRIDGE_NOT_HANDLED;
+
+ if (ret == PCI_SW_BRIDGE_NOT_HANDLED)
+ *value = *((u32*) &bridge->pcie_conf + reg / 4);
+ } else {
+ if (bridge->ops->read_base)
+ ret = bridge->ops->read_base(bridge, reg, value);
+ else
+ ret = PCI_SW_BRIDGE_NOT_HANDLED;
+
+ if (ret == PCI_SW_BRIDGE_NOT_HANDLED)
+ *value = *((u32*) &bridge->conf + reg / 4);
I'm not sure about this part, where the config space that's not
handled by the bridge's ops ends up just being RAM that's readable and
writable with no effect on the hardware. That seems a little
counter-intuitive. I think dropping writes and reading zeroes would
simplify my mental model.
The fact that it is handled just like RAM is actually needed for a
number of registers. For example, in the current pci-mvebu driver, when
reading we have:
case PCI_VENDOR_ID:
*value = bridge->device << 16 | bridge->vendor;
break;
case PCI_COMMAND:
*value = bridge->command | bridge->status << 16;
break;
case PCI_CLASS_REVISION:
*value = bridge->class << 16 | bridge->interface << 8 |
bridge->revision;
break;
case PCI_CACHE_LINE_SIZE:
*value = bridge->bist << 24 | bridge->header_type << 16 |
bridge->latency_timer << 8 | bridge->cache_line_size;
break;
case PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_1:
*value = bridge->bar[((where & ~3) - PCI_BASE_ADDRESS_0) / 4];
break;
case PCI_PRIMARY_BUS:
*value = (bridge->secondary_latency_timer << 24 |
bridge->subordinate_bus << 16 |
bridge->secondary_bus << 8 |
bridge->primary_bus);
break;
case PCI_IO_BASE:
if (!mvebu_has_ioport(port))
*value = bridge->secondary_status << 16;
else
*value = (bridge->secondary_status << 16 |
bridge->iolimit << 8 |
bridge->iobase);
break;
We definitely cannot return zeroes for all these values.
And for writes, we have code like this:
case PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_1:
bridge->bar[((where & ~3) - PCI_BASE_ADDRESS_0) / 4] = value;
break;
case PCI_IO_BASE:
/*
* We also keep bit 1 set, it is a read-only bit that
* indicates we support 32 bits addressing for the
* I/O
*/
bridge->iobase = (value & 0xff) | PCI_IO_RANGE_TYPE_32;
bridge->iolimit = ((value >> 8) & 0xff) | PCI_IO_RANGE_TYPE_32;
mvebu_pcie_handle_iobase_change(port);
break;
case PCI_MEMORY_BASE:
bridge->membase = value & 0xffff;
bridge->memlimit = value >> 16;
mvebu_pcie_handle_membase_change(port);
break;
case PCI_IO_BASE_UPPER16:
bridge->iobaseupper = value & 0xffff;
bridge->iolimitupper = value >> 16;
mvebu_pcie_handle_iobase_change(port);
break;
case PCI_PRIMARY_BUS:
bridge->primary_bus = value & 0xff;
bridge->secondary_bus = (value >> 8) & 0xff;
bridge->subordinate_bus = (value >> 16) & 0xff;
bridge->secondary_latency_timer = (value >> 24) & 0xff;
mvebu_pcie_set_local_bus_nr(port, bridge->secondary_bus);
break;
The writes being made to those parts of the config space are being
reflected back when reading the config space later.
This isn't directly related to your patches, but this is a common
pattern with some variations between drivers, which makes me wonder
whether there are bugs lurking. These use "where & 3" for size 2:
advk_pcie_rd_conf()
mtk_pcie_hw_rd_cfg()
pci_generic_config_read32()
But these use "where & 2":
_altera_pcie_cfg_read()
rcar_pcie_read_conf()
I *think* this means that unaligned 2-byte config reads on Altera and
R-Car will return incorrect data. In both cases, the actual read seen
by the hardware is a 32-bit read, but I think we extract the wrong 16
bits from that result.
Indeed, if unaligned 2-byte config reads are made, they will return
bogus results. But are such reads allowed ?
I wonder if there's a way to use a common helper function to do this.
Yes, this small bit of logic is duplicated all over the place. I'll see
if I can come up with some reasonable helpers for that.
quoted
+ return PCIBIOS_SUCCESSFUL;
+}
quoted
+ /*
+ * Called when writing to the regular PCI bridge configuration
+ * space. old is the current value, new is the new value being
+ * written, and mask indicates which parts of the value are
+ * being changed.
+ */
+ void (*write_base)(struct pci_sw_bridge *bridge, int reg,
+ u32 old, u32 new, u32 mask);
+
+ /*
+ * Same as ->read_base(), except it is for reading from the
+ * PCIe capability configuration space.
I think you mean "->write_base(), except it is for writing to"
Indeed, yes. Will fix that.
So, to sum up, there are really three key questions:
(1) Which name do you want for this ?
(2) Agreeing on whether a RAM-like behavior is acceptable, and if not,
which solution do you propose instead.
(3) How much do you want me to convert other drivers vs. the
maintainers for those drivers being responsible for doing this.
Since "There are only two hard things in Computer Science: cache
invalidation and naming things.", I expect question (1) to be the most
difficult one :-)
Best regards,
Thomas
--
Thomas Petazzoni, CTO, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com
From: linux@armlinux.org.uk (Russell King - ARM Linux) Date: 2018-08-01 09:21:19
On Wed, Aug 01, 2018 at 10:49:57AM +0200, Thomas Petazzoni wrote:
Indeed, yes. Will fix that.
So, to sum up, there are really three key questions:
(1) Which name do you want for this ?
(2) Agreeing on whether a RAM-like behavior is acceptable, and if not,
which solution do you propose instead.
Please take the time to read the PCI(e) specifications and implement
what it recommends, rather than doing something else. That's what I
did when I originally reworked the mvebu PCIe driver for Armada 388,
and it's the only sensible approach to have something that works with
the rest of the Linux PCI(e) layer.
Going off and implementing a non-standard behaviour is a recipe for
things breaking as the PCIe kernel support evolves.
The spec requires reserved registers to read back as zero and ignore
writes, whereas implemented registers have a mixture of behaviours:
- read/write
- read, write to clear
- read-only
Here's the extract(s):
All PCI devices must treat Configuration Space write operations
to reserved registers as no-ops; that is, the access must be
completed normally on the bus and the data discarded. Read
accesses to reserved or unimplemented registers must be completed
normally and a data value of 0 returned.
(eg) PCI status:
Reserved bits should be read-only and return zero when read.
A one bit is reset (if it is not read-only) whenever the register
is written, and the write data in the corresponding bit location
is a 1.
[which is why doing the read-modify-write action that some host
bridges that only support 32-bit accesses is dangerous - it leads
to various status bits being inadvertently reset.]
Getting this right in a software emulation of the register space for
each bit in every register makes for a happy Linux PCIe layer.
Now, configuration read/writes use naturally aligned addresses. The
PCI specification defines the PC IO 0xcf8/0xcfc configuration access
mechanism. The first register defines the address with a 6 bit
"double-word" address to cover the 256 bytes of standard config space.
Accesses to 0xcfc are forwarded to the PCI bus as a single
configuration request - and that means there is nothing to deal with
an attempt to access a mis-aligned word.
Indeed, if 0xcfe was to be accessed as a double word, this would
result in the processor reading the two bytes from IO address 0xcfe,
0xcff, as well as 0xd00 and 0xd01 which are not part of the
configuration data register - resulting in undefined data being read.
So, basically, misaligned configuration accesses are not supported.
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 13.8Mbps down 630kbps up
According to speedtest.net: 13Mbps down 490kbps up
Hello Russell,
Thanks for your feedback.
On Wed, 1 Aug 2018 10:21:19 +0100, Russell King - ARM Linux wrote:
quoted
(2) Agreeing on whether a RAM-like behavior is acceptable, and if not,
which solution do you propose instead.
Please take the time to read the PCI(e) specifications and implement
what it recommends, rather than doing something else. That's what I
did when I originally reworked the mvebu PCIe driver for Armada 388,
and it's the only sensible approach to have something that works with
the rest of the Linux PCI(e) layer.
Actually, all what I'm doing is generalizing what the mvebu PCIe driver
is doing (which you significantly contributed) to, and try to make it
usable by other drivers that have the same need.
So, I'm precisely trying to make sure we implement this stuff once,
correctly, rather than duplicate it in several drivers. I'm not
pretending that my first iteration is entirely correct, and your review
of it to make it better is much appreciated.
Going off and implementing a non-standard behaviour is a recipe for
things breaking as the PCIe kernel support evolves.
Sure.
The spec requires reserved registers to read back as zero and ignore
writes, whereas implemented registers have a mixture of behaviours:
- read/write
- read, write to clear
- read-only
Here's the extract(s):
All PCI devices must treat Configuration Space write operations
to reserved registers as no-ops; that is, the access must be
completed normally on the bus and the data discarded. Read
accesses to reserved or unimplemented registers must be completed
normally and a data value of 0 returned.
(eg) PCI status:
Reserved bits should be read-only and return zero when read.
A one bit is reset (if it is not read-only) whenever the register
is written, and the write data in the corresponding bit location
is a 1.
Right. This pci-sw-bridge layer should implement this behavior.
[which is why doing the read-modify-write action that some host
bridges that only support 32-bit accesses is dangerous - it leads
to various status bits being inadvertently reset.]
Getting this right in a software emulation of the register space for
each bit in every register makes for a happy Linux PCIe layer.
Absolutely.
Now, configuration read/writes use naturally aligned addresses. The
PCI specification defines the PC IO 0xcf8/0xcfc configuration access
mechanism. The first register defines the address with a 6 bit
"double-word" address to cover the 256 bytes of standard config space.
Accesses to 0xcfc are forwarded to the PCI bus as a single
configuration request - and that means there is nothing to deal with
an attempt to access a mis-aligned word.
Indeed, if 0xcfe was to be accessed as a double word, this would
result in the processor reading the two bytes from IO address 0xcfe,
0xcff, as well as 0xd00 and 0xd01 which are not part of the
configuration data register - resulting in undefined data being read.
So, basically, misaligned configuration accesses are not supported.
OK. So the where & 2 that the RCAR driver is doing for 2 bytes accesses
is OK, because bit 0 of where will always be 0 for a 2 bytes access.
Thanks,
Thomas Petazzoni
--
Thomas Petazzoni, CTO, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com
Hello Russell,
On Wed, 1 Aug 2018 10:21:19 +0100, Russell King - ARM Linux wrote:
All PCI devices must treat Configuration Space write operations
to reserved registers as no-ops; that is, the access must be
completed normally on the bus and the data discarded. Read
accesses to reserved or unimplemented registers must be completed
normally and a data value of 0 returned.
(eg) PCI status:
Reserved bits should be read-only and return zero when read.
A one bit is reset (if it is not read-only) whenever the register
is written, and the write data in the corresponding bit location
is a 1.
[which is why doing the read-modify-write action that some host
bridges that only support 32-bit accesses is dangerous - it leads
to various status bits being inadvertently reset.]
Speaking of this, the generic pci_generic_config_write32() function
indeed does this incorrectly, and prints a warning.
However, I just looked at the pci-thunder-pem code, and it seems to
correctly account for those W1C bits, by having an explicit list of
registers and their W1C bits (see thunder_pem_bridge_w1c_bits). This
isn't specific to the Thunder PCIe controller at all, and would benefit
from being made generic, no ?
Best regards,
Thomas
--
Thomas Petazzoni, CTO, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com
This commit convers the pci-mvebu driver to use the recently
introduced pci-sw-bridge logic, that helps emulating a root port PCI
bridge.
It has been tested on Armada GP XP, with a E1000E NIC.
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
drivers/pci/controller/Kconfig | 1 +
drivers/pci/controller/pci-mvebu.c | 370 ++++++++++---------------------------
2 files changed, 102 insertions(+), 269 deletions(-)
@@ -452,10 +399,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)staticvoidmvebu_pcie_handle_membase_change(structmvebu_pcie_port*port){structmvebu_pcie_windowdesired={.remap=MVEBU_MBUS_NO_REMAP};+structpci_sw_bridge_conf*conf=&port->bridge.conf;/* Are the new membase/memlimit values invalid? */-if(port->bridge.memlimit<port->bridge.membase||-!(port->bridge.command&PCI_COMMAND_MEMORY)){+if(conf->memlimit<conf->membase||+!(conf->command&PCI_COMMAND_MEMORY)){mvebu_pcie_set_window(port,port->mem_target,port->mem_attr,&desired,&port->memwin);return;
@@ -467,130 +415,34 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)*windowtosetup,accordingtothePCI-to-PCIbridge*specifications.*/-desired.base=((port->bridge.membase&0xFFF0)<<16);-desired.size=(((port->bridge.memlimit&0xFFF0)<<16)|0xFFFFF)-+desired.base=((conf->membase&0xFFF0)<<16);+desired.size=(((conf->memlimit&0xFFF0)<<16)|0xFFFFF)-desired.base+1;mvebu_pcie_set_window(port,port->mem_target,port->mem_attr,&desired,&port->memwin);}-/*-*InitializetheconfigurationspaceofthePCI-to-PCIbridge-*associatedwiththegivenPCIeinterface.-*/-staticvoidmvebu_sw_pci_bridge_init(structmvebu_pcie_port*port)+staticpci_sw_bridge_read_status_t+mvebu_pci_sw_bridge_pcie_read(structpci_sw_bridge*bridge,+intreg,u32*value){-structmvebu_sw_pci_bridge*bridge=&port->bridge;--memset(bridge,0,sizeof(structmvebu_sw_pci_bridge));--bridge->class=PCI_CLASS_BRIDGE_PCI;-bridge->vendor=PCI_VENDOR_ID_MARVELL;-bridge->device=mvebu_readl(port,PCIE_DEV_ID_OFF)>>16;-bridge->revision=mvebu_readl(port,PCIE_DEV_REV_OFF)&0xff;-bridge->header_type=PCI_HEADER_TYPE_BRIDGE;-bridge->cache_line_size=0x10;--/* We support 32 bits I/O addressing */-bridge->iobase=PCI_IO_RANGE_TYPE_32;-bridge->iolimit=PCI_IO_RANGE_TYPE_32;--/* Add capabilities */-bridge->status=PCI_STATUS_CAP_LIST;-}--/*-*ReadtheconfigurationspaceofthePCI-to-PCIbridgeassociatedto-*thegivenPCIeinterface.-*/-staticintmvebu_sw_pci_bridge_read(structmvebu_pcie_port*port,-unsignedintwhere,intsize,u32*value)-{-structmvebu_sw_pci_bridge*bridge=&port->bridge;--switch(where&~3){-casePCI_VENDOR_ID:-*value=bridge->device<<16|bridge->vendor;-break;--casePCI_COMMAND:-*value=bridge->command|bridge->status<<16;-break;--casePCI_CLASS_REVISION:-*value=bridge->class<<16|bridge->interface<<8|-bridge->revision;-break;--casePCI_CACHE_LINE_SIZE:-*value=bridge->bist<<24|bridge->header_type<<16|-bridge->latency_timer<<8|bridge->cache_line_size;-break;--casePCI_BASE_ADDRESS_0...PCI_BASE_ADDRESS_1:-*value=bridge->bar[((where&~3)-PCI_BASE_ADDRESS_0)/4];-break;--casePCI_PRIMARY_BUS:-*value=(bridge->secondary_latency_timer<<24|-bridge->subordinate_bus<<16|-bridge->secondary_bus<<8|-bridge->primary_bus);-break;--casePCI_IO_BASE:-if(!mvebu_has_ioport(port))-*value=bridge->secondary_status<<16;-else-*value=(bridge->secondary_status<<16|-bridge->iolimit<<8|-bridge->iobase);-break;--casePCI_MEMORY_BASE:-*value=(bridge->memlimit<<16|bridge->membase);-break;--casePCI_PREF_MEMORY_BASE:-*value=0;-break;--casePCI_IO_BASE_UPPER16:-*value=(bridge->iolimitupper<<16|bridge->iobaseupper);-break;--casePCI_CAPABILITY_LIST:-*value=PCISWCAP;-break;+structmvebu_pcie_port*port=bridge->data;-casePCI_ROM_ADDRESS1:-*value=0;-break;--casePCI_INTERRUPT_LINE:-/* LINE PIN MIN_GNT MAX_LAT */-*value=0;-break;--casePCISWCAP_EXP_LIST_ID:-/* Set PCIe v2, root port, slot support */-*value=(PCI_EXP_TYPE_ROOT_PORT<<4|2|-PCI_EXP_FLAGS_SLOT)<<16|PCI_CAP_ID_EXP;-break;--casePCISWCAP_EXP_DEVCAP:+switch(reg){+casePCI_EXP_DEVCAP:*value=mvebu_readl(port,PCIE_CAP_PCIEXP+PCI_EXP_DEVCAP);break;-casePCISWCAP_EXP_DEVCTL:+casePCI_EXP_DEVCTL:*value=mvebu_readl(port,PCIE_CAP_PCIEXP+PCI_EXP_DEVCTL)&~(PCI_EXP_DEVCTL_URRE|PCI_EXP_DEVCTL_FERE|PCI_EXP_DEVCTL_NFERE|PCI_EXP_DEVCTL_CERE);-*value|=bridge->pcie_devctl;+/* FIXME */+*value|=bridge->pcie_conf.devctl;break;-casePCISWCAP_EXP_LNKCAP:+casePCI_EXP_LNKCAP:/**PCIerequirestheclockpowermanagementcapabilitytobe*hard-wiredtozerofordownstreamports
@@ -599,132 +451,86 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,~PCI_EXP_LNKCAP_CLKPM;break;-casePCISWCAP_EXP_LNKCTL:+casePCI_EXP_LNKCTL:*value=mvebu_readl(port,PCIE_CAP_PCIEXP+PCI_EXP_LNKCTL);break;-casePCISWCAP_EXP_SLTCAP:-*value=bridge->pcie_sltcap;-break;--casePCISWCAP_EXP_SLTCTL:+casePCI_EXP_SLTCTL:*value=PCI_EXP_SLTSTA_PDS<<16;break;-casePCISWCAP_EXP_RTCTL:-*value=bridge->pcie_rtctl;-break;--casePCISWCAP_EXP_RTSTA:+casePCI_EXP_RTSTA:*value=mvebu_readl(port,PCIE_RC_RTSTA);break;-/* PCIe requires the v2 fields to be hard-wired to zero */-casePCISWCAP_EXP_DEVCAP2:-casePCISWCAP_EXP_DEVCTL2:-casePCISWCAP_EXP_LNKCAP2:-casePCISWCAP_EXP_LNKCTL2:-casePCISWCAP_EXP_SLTCAP2:-casePCISWCAP_EXP_SLTCTL2:default:-/*-*PCIdefinesconfigurationreadaccessestoreservedor-*unimplementedregisterstoreadaszeroandcomplete-*normally.-*/-*value=0;-returnPCIBIOS_SUCCESSFUL;+returnPCI_SW_BRIDGE_NOT_HANDLED;}-if(size==2)-*value=(*value>>(8*(where&3)))&0xffff;-elseif(size==1)-*value=(*value>>(8*(where&3)))&0xff;--returnPCIBIOS_SUCCESSFUL;+returnPCI_SW_BRIDGE_HANDLED;}-/* Write to the PCI-to-PCI bridge configuration space */-staticintmvebu_sw_pci_bridge_write(structmvebu_pcie_port*port,-unsignedintwhere,intsize,u32value)+staticvoidmvebu_pci_sw_bridge_base_write(structpci_sw_bridge*bridge,+intreg,u32old,u32new,u32mask){-structmvebu_sw_pci_bridge*bridge=&port->bridge;-u32mask,reg;-interr;--if(size==4)-mask=0x0;-elseif(size==2)-mask=~(0xffff<<((where&3)*8));-elseif(size==1)-mask=~(0xff<<((where&3)*8));-else-returnPCIBIOS_BAD_REGISTER_NUMBER;--err=mvebu_sw_pci_bridge_read(port,where&~3,4,®);-if(err)-returnerr;+structmvebu_pcie_port*port=bridge->data;+structpci_sw_bridge_conf*conf=&bridge->conf;-value=(reg&mask)|value<<((where&3)*8);--switch(where&~3){+switch(reg){casePCI_COMMAND:{-u32old=bridge->command;-if(!mvebu_has_ioport(port))-value&=~PCI_COMMAND_IO;+conf->command&=~PCI_COMMAND_IO;-bridge->command=value&0xffff;-if((old^bridge->command)&PCI_COMMAND_IO)+if((old^new)&PCI_COMMAND_IO)mvebu_pcie_handle_iobase_change(port);-if((old^bridge->command)&PCI_COMMAND_MEMORY)+if((old^new)&PCI_COMMAND_MEMORY)mvebu_pcie_handle_membase_change(port);-break;-}-casePCI_BASE_ADDRESS_0...PCI_BASE_ADDRESS_1:-bridge->bar[((where&~3)-PCI_BASE_ADDRESS_0)/4]=value;break;+}casePCI_IO_BASE:/*-*Wealsokeepbit1set,itisaread-onlybitthat+*Wekeepbit1set,itisaread-onlybitthat*indicateswesupport32bitsaddressingforthe*I/O*/-bridge->iobase=(value&0xff)|PCI_IO_RANGE_TYPE_32;-bridge->iolimit=((value>>8)&0xff)|PCI_IO_RANGE_TYPE_32;+conf->iobase|=PCI_IO_RANGE_TYPE_32;+conf->iolimit|=PCI_IO_RANGE_TYPE_32;mvebu_pcie_handle_iobase_change(port);break;casePCI_MEMORY_BASE:-bridge->membase=value&0xffff;-bridge->memlimit=value>>16;mvebu_pcie_handle_membase_change(port);break;casePCI_IO_BASE_UPPER16:-bridge->iobaseupper=value&0xffff;-bridge->iolimitupper=value>>16;mvebu_pcie_handle_iobase_change(port);break;casePCI_PRIMARY_BUS:-bridge->primary_bus=value&0xff;-bridge->secondary_bus=(value>>8)&0xff;-bridge->subordinate_bus=(value>>16)&0xff;-bridge->secondary_latency_timer=(value>>24)&0xff;-mvebu_pcie_set_local_bus_nr(port,bridge->secondary_bus);+mvebu_pcie_set_local_bus_nr(port,conf->secondary_bus);break;-casePCISWCAP_EXP_DEVCTL:+default:+break;+}+}++staticvoidmvebu_pci_sw_bridge_pcie_write(structpci_sw_bridge*bridge,+intreg,u32old,u32new,u32mask)+{+structmvebu_pcie_port*port=bridge->data;++switch(reg){+casePCI_EXP_DEVCTL:/**Armada370datasaysthesebitsmustalways*bezerowheninrootcomplexmode.*/-value&=~(PCI_EXP_DEVCTL_URRE|PCI_EXP_DEVCTL_FERE|-PCI_EXP_DEVCTL_NFERE|PCI_EXP_DEVCTL_CERE);+new&=~(PCI_EXP_DEVCTL_URRE|PCI_EXP_DEVCTL_FERE|+PCI_EXP_DEVCTL_NFERE|PCI_EXP_DEVCTL_CERE);/**Ifthemaskis0xffff0000,thenweonlywanttowrite
@@ -732,20 +538,20 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,*RW1Cbitsinthedevicestatusregister.Maskoutthe*statusregisterbits.*/-if(mask==0xffff0000)-value&=0xffff;+if(new==0xffff0000)+new&=0xffff;-mvebu_writel(port,value,PCIE_CAP_PCIEXP+PCI_EXP_DEVCTL);+mvebu_writel(port,new,PCIE_CAP_PCIEXP+PCI_EXP_DEVCTL);break;-casePCISWCAP_EXP_LNKCTL:+casePCI_EXP_LNKCTL:/**Ifwedon'tsupportCLKREQ,wemustensurethatthe*CLKREQenablebitalwaysreadszero.Sincewehaven't*hadthiscapability,andit'sdependentonboardwiring,*disableitforthetimebeing.*/-value&=~PCI_EXP_LNKCTL_CLKREQ_EN;+new&=~PCI_EXP_LNKCTL_CLKREQ_EN;/**Ifthemaskis0xffff0000,thenweonlywanttowrite
@@ -754,21 +560,47 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,*RW1Cstatusregisterbits.*/if(mask==0xffff0000)-value&=~((PCI_EXP_LNKSTA_LABS|-PCI_EXP_LNKSTA_LBMS)<<16);+new&=~((PCI_EXP_LNKSTA_LABS|+PCI_EXP_LNKSTA_LBMS)<<16);-mvebu_writel(port,value,PCIE_CAP_PCIEXP+PCI_EXP_LNKCTL);+mvebu_writel(port,new,PCIE_CAP_PCIEXP+PCI_EXP_LNKCTL);break;-casePCISWCAP_EXP_RTSTA:-mvebu_writel(port,value,PCIE_RC_RTSTA);+casePCI_EXP_RTSTA:+mvebu_writel(port,new,PCIE_RC_RTSTA);break;+}+}-default:-break;+structpci_sw_bridge_opsmvebu_pci_sw_bridge_ops={+.write_base=mvebu_pci_sw_bridge_base_write,+.read_pcie=mvebu_pci_sw_bridge_pcie_read,+.write_pcie=mvebu_pci_sw_bridge_pcie_write,+};++/*+*InitializetheconfigurationspaceofthePCI-to-PCIbridge+*associatedwiththegivenPCIeinterface.+*/+staticvoidmvebu_pci_sw_bridge_init(structmvebu_pcie_port*port)+{+structpci_sw_bridge*bridge=&port->bridge;++bridge->conf.vendor=PCI_VENDOR_ID_MARVELL;+bridge->conf.device=mvebu_readl(port,PCIE_DEV_ID_OFF)>>16;+bridge->conf.revision=mvebu_readl(port,PCIE_DEV_REV_OFF)&0xff;++if(mvebu_has_ioport(port)){+/* We support 32 bits I/O addressing */+bridge->conf.iobase=PCI_IO_RANGE_TYPE_32;+bridge->conf.iolimit=PCI_IO_RANGE_TYPE_32;}-returnPCIBIOS_SUCCESSFUL;+bridge->has_pcie=true;+bridge->data=port;+bridge->ops=&mvebu_pci_sw_bridge_ops;++pci_sw_bridge_init(bridge);}staticinlinestructmvebu_pcie*sys_to_pcie(structpci_sys_data*sys)
From: Zachary Zhang <redacted>
The PCI controller in the Marvell Armada 3720 does not implement a
root port PCI bridge at the hardware level. This causes a number of
problems when using PCIe switches or when the Max Payload size needs
to be aligned between the root complex and the endpoint.
Implementing an emulated root PCI bridge, like is already done in the
pci-mvebu driver for older Marvell platforms allows to solve those
issues, and also to support features such as ASR, PME, VC, HP.
Signed-off-by: Zachary Zhang <redacted>
[Thomas: convert to the common PCI software bridge logic.]
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
drivers/pci/controller/Kconfig | 1 +
drivers/pci/controller/pci-aardvark.c | 119 +++++++++++++++++++++++++++++++++-
2 files changed, 117 insertions(+), 3 deletions(-)
@@ -433,6 +443,101 @@ static int advk_pcie_wait_pio(struct advk_pcie *pcie)return-ETIMEDOUT;}+staticpci_sw_bridge_read_status_t+advk_pci_sw_bridge_pcie_read(structpci_sw_bridge*bridge,+intreg,u32*value)+{+structadvk_pcie*pcie=bridge->data;++switch(reg){+casePCI_EXP_SLTCTL:+*value=PCI_EXP_SLTSTA_PDS<<16;+returnPCI_SW_BRIDGE_HANDLED;++casePCI_EXP_RTCTL:+*value=(advk_readl(pcie,PCIE_ISR0_MASK_REG)&PCIE_MSG_PM_PME_MASK)?+PCI_EXP_RTCTL_PMEIE:0;+returnPCI_SW_BRIDGE_HANDLED;++casePCI_EXP_RTSTA:+*value=(advk_readl(pcie,PCIE_ISR0_REG)&PCIE_MSG_PM_PME_MASK)<<16|+(advk_readl(pcie,PCIE_MSG_LOG_REG)>>16);+returnPCI_SW_BRIDGE_HANDLED;++casePCI_CAP_LIST_ID:+casePCI_EXP_DEVCAP:+casePCI_EXP_DEVCTL:+casePCI_EXP_LNKCAP:+casePCI_EXP_LNKCTL:+*value=advk_readl(pcie,PCIE_CORE_PCIEXP_CAP+reg);+returnPCI_SW_BRIDGE_HANDLED;+default:+returnPCI_SW_BRIDGE_NOT_HANDLED;+}++}++staticvoidadvk_pci_sw_bridge_pcie_write(structpci_sw_bridge*bridge,+intreg,u32old,u32new,u32mask)+{+structadvk_pcie*pcie=bridge->data;++switch(reg){+casePCI_EXP_DEVCTL:+casePCI_EXP_LNKCTL:+advk_writel(pcie,new,PCIE_CORE_PCIEXP_CAP+reg);+break;++casePCI_EXP_RTCTL:+new=(new&PCI_EXP_RTCTL_PMEIE)<<3;+advk_writel(pcie,new,PCIE_ISR0_MASK_REG);+break;++casePCI_EXP_RTSTA:+new=(new&PCI_EXP_RTSTA_PME)>>9;+advk_writel(pcie,new,PCIE_ISR0_REG);+break;++default:+break;+}+}++structpci_sw_bridge_opsadvk_pci_sw_bridge_ops={+.read_pcie=advk_pci_sw_bridge_pcie_read,+.write_pcie=advk_pci_sw_bridge_pcie_write,+};++/*+*InitializetheconfigurationspaceofthePCI-to-PCIbridge+*associatedwiththegivenPCIeinterface.+*/+staticvoidadvk_sw_pci_bridge_init(structadvk_pcie*pcie)+{+structpci_sw_bridge*bridge=&pcie->bridge;++bridge->conf.vendor=advk_readl(pcie,PCIE_CORE_DEV_ID_REG)&0xffff;+bridge->conf.device=advk_readl(pcie,PCIE_CORE_DEV_ID_REG)>>16;+bridge->conf.revision=advk_readl(pcie,PCIE_CORE_DEV_REV_REG)&0xff;++/* Support 32 bits I/O addressing */+bridge->conf.iobase=PCI_IO_RANGE_TYPE_32;+bridge->conf.iolimit=PCI_IO_RANGE_TYPE_32;++/* Support 64 bits memory pref */+bridge->conf.pref_mem_base=PCI_PREF_RANGE_TYPE_64;+bridge->conf.pref_mem_limit=PCI_PREF_RANGE_TYPE_64;++/* Support interrupt A for MSI feature */+bridge->conf.intpin=PCIE_CORE_INT_A_ASSERT_ENABLE;++bridge->has_pcie=true;+bridge->data=pcie;+bridge->ops=&advk_pci_sw_bridge_ops;++pci_sw_bridge_init(bridge);+}+staticintadvk_pcie_rd_conf(structpci_bus*bus,u32devfn,intwhere,intsize,u32*val){
@@ -445,6 +550,9 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn,returnPCIBIOS_DEVICE_NOT_FOUND;}+if(bus->number==pcie->root_bus_nr)+returnpci_sw_bridge_read(&pcie->bridge,where,size,val);+/* Start PIO */advk_writel(pcie,0,PIO_START);advk_writel(pcie,1,PIO_ISR);
@@ -452,7 +560,7 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn,/* Program the control register */reg=advk_readl(pcie,PIO_CTRL);reg&=~PIO_CTRL_TYPE_MASK;-if(bus->number==pcie->root_bus_nr)+if(bus->primary==pcie->root_bus_nr)reg|=PCIE_CONFIG_RD_TYPE0;elsereg|=PCIE_CONFIG_RD_TYPE1;
@@ -507,7 +618,7 @@ static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn,/* Program the control register */reg=advk_readl(pcie,PIO_CTRL);reg&=~PIO_CTRL_TYPE_MASK;-if(bus->number==pcie->root_bus_nr)+if(bus->primary==pcie->root_bus_nr)reg|=PCIE_CONFIG_WR_TYPE0;elsereg|=PCIE_CONFIG_WR_TYPE1;
@@ -922,6 +1033,8 @@ static int advk_pcie_probe(struct platform_device *pdev)advk_pcie_setup_hw(pcie);+advk_sw_pci_bridge_init(pcie);+ret=advk_pcie_init_irq_domain(pcie);if(ret){dev_err(dev,"Failed to initialize irq\n");
+ /* Support interrupt A for MSI feature */
+ bridge->conf.intpin = PCIE_CORE_INT_A_ASSERT_ENABLE;
Only 3.5 years later, IIUC, this is the value you get when you read
PCI_INTERRUPT_PIN, so I think this should be PCI_INTERRUPT_INTA, not
PCIE_CORE_INT_A_ASSERT_ENABLE.
Readers expect to get the values defined in the PCI spec, i.e.,
PCI_INTERRUPT_UNKNOWN
PCI_INTERRUPT_INTA
PCI_INTERRUPT_INTB
PCI_INTERRUPT_INTC
PCI_INTERRUPT_INTD
Bjorn
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
+ /* Support interrupt A for MSI feature */
+ bridge->conf.intpin = PCIE_CORE_INT_A_ASSERT_ENABLE;
Only 3.5 years later, IIUC, this is the value you get when you read
PCI_INTERRUPT_PIN, so I think this should be PCI_INTERRUPT_INTA, not
PCIE_CORE_INT_A_ASSERT_ENABLE.
Readers expect to get the values defined in the PCI spec, i.e.,
PCI_INTERRUPT_UNKNOWN
PCI_INTERRUPT_INTA
PCI_INTERRUPT_INTB
PCI_INTERRUPT_INTC
PCI_INTERRUPT_INTD
Bjorn
+ /* Support interrupt A for MSI feature */
+ bridge->conf.intpin = PCIE_CORE_INT_A_ASSERT_ENABLE;
Only 3.5 years later, IIUC, this is the value you get when you read
PCI_INTERRUPT_PIN, so I think this should be PCI_INTERRUPT_INTA, not
PCIE_CORE_INT_A_ASSERT_ENABLE.
Readers expect to get the values defined in the PCI spec, i.e.,
PCI_INTERRUPT_UNKNOWN
PCI_INTERRUPT_INTA
PCI_INTERRUPT_INTB
PCI_INTERRUPT_INTC
PCI_INTERRUPT_INTD
Bjorn
+ /* Support interrupt A for MSI feature */
+ bridge->conf.intpin = PCIE_CORE_INT_A_ASSERT_ENABLE;
Only 3.5 years later, IIUC, this is the value you get when you read
PCI_INTERRUPT_PIN, so I think this should be PCI_INTERRUPT_INTA, not
PCIE_CORE_INT_A_ASSERT_ENABLE.
Readers expect to get the values defined in the PCI spec, i.e.,
PCI_INTERRUPT_UNKNOWN
PCI_INTERRUPT_INTA
PCI_INTERRUPT_INTB
PCI_INTERRUPT_INTC
PCI_INTERRUPT_INTD
Bjorn, Lorenzo,
On Fri, 29 Jun 2018 11:22:28 +0200, Thomas Petazzoni wrote:
The pci-mvebu driver already contains some logic to emulate a root
port PCI bridge. It turns out that we have a similar need for the
pci-aardvark driver. Instead of duplicating the same logic in two
drivers, this patch series starts by adding a small common
infrastructure that helps emulate a root port PCI bridge, converts
pci-mvebu to use it, and finally extends pci-aardvark to use it as
well.
Thanks to this, Marvell Armada 3720 based systems, which use the
Aarkvark PCI controller, will have better PCI support, by having a
root port PCI bridge exposed.
The emulated PCI bridge common logic is a proposal, I very much
welcome comments and suggestions. Also, if you feel that adding a
common logic for only two drivers is too early, I'm fine with
duplicating a bit of code betwen pci-mvebu and pci-aardvark.
I was wondering if you had any feedback on this patch series. Not
necessarily a detailed review, but at least some general
feeling/feedback on the overall approach would be very nice.
Thanks a lot,
Thomas Petazzoni
--
Thomas Petazzoni, CTO, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com