All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv2 0/3] PCI: emulated PCI bridge config space
@ 2018-09-12 15:48 ` Thomas Petazzoni
  0 siblings, 0 replies; 24+ messages in thread
From: Thomas Petazzoni @ 2018-09-12 15:48 UTC (permalink / raw)
  To: Bjorn Helgaas, Lorenzo Pieralisi, Russell King
  Cc: Antoine Tenart, linux-pci, Gregory Clement, Maxime Chevallier,
	Nadav Haklai, Thomas Petazzoni, Miquèl Raynal,
	linux-arm-kernel

Hello,

The pci-mvebu driver already contains some logic to emulate a root
port PCI bridge configuration space. 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 config
space, 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.

In this version 2, I have (hopefully) taken into account the comments
from Bjorn Helgaas and Russell King. Here are the changes:

 - Rebased to 4.19-rc1

 - The functions implementing the config space read/write emulation
   now have the knowledge of which registers/bits are read-only,
   read-write, reserved and write-1-to-clear, and behave according to
   this information.

 - I have renamed the function to use conf_read() and conf_write()
   suffixes, as suggested by Bjorn.

 - The whole thing is now named pci-bridge-emul instead of
   pci-sw-bridge (and of course all functions, types and macros were
   changed accordingly), as Bjorn wasn't happy about pci-sw-bridge.

 - The header file was moved into drivers/pci (instead of being in
   include/linux).

Thanks a lot in advance for your review and feedback.

Best regards,

Thomas Petazzoni

Thomas Petazzoni (2):
  PCI: Introduce PCI bridge emulated config space common logic
  PCI: mvebu: Convert to PCI emulated bridge config space

Zachary Zhang (1):
  PCI: aardvark: Implement emulated root PCI bridge config space

 drivers/pci/Kconfig                   |   3 +
 drivers/pci/Makefile                  |   1 +
 drivers/pci/controller/Kconfig        |   2 +
 drivers/pci/controller/pci-aardvark.c | 129 ++++++++++-
 drivers/pci/controller/pci-mvebu.c    | 375 +++++++++----------------------
 drivers/pci/pci-bridge-emul.c         | 408 ++++++++++++++++++++++++++++++++++
 drivers/pci/pci-bridge-emul.h         | 124 +++++++++++
 7 files changed, 770 insertions(+), 272 deletions(-)
 create mode 100644 drivers/pci/pci-bridge-emul.c
 create mode 100644 drivers/pci/pci-bridge-emul.h

-- 
2.14.4


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCHv2 0/3] PCI: emulated PCI bridge config space
@ 2018-09-12 15:48 ` Thomas Petazzoni
  0 siblings, 0 replies; 24+ messages in thread
From: Thomas Petazzoni @ 2018-09-12 15:48 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

The pci-mvebu driver already contains some logic to emulate a root
port PCI bridge configuration space. 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 config
space, 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.

In this version 2, I have (hopefully) taken into account the comments
from Bjorn Helgaas and Russell King. Here are the changes:

 - Rebased to 4.19-rc1

 - The functions implementing the config space read/write emulation
   now have the knowledge of which registers/bits are read-only,
   read-write, reserved and write-1-to-clear, and behave according to
   this information.

 - I have renamed the function to use conf_read() and conf_write()
   suffixes, as suggested by Bjorn.

 - The whole thing is now named pci-bridge-emul instead of
   pci-sw-bridge (and of course all functions, types and macros were
   changed accordingly), as Bjorn wasn't happy about pci-sw-bridge.

 - The header file was moved into drivers/pci (instead of being in
   include/linux).

Thanks a lot in advance for your review and feedback.

Best regards,

Thomas Petazzoni

Thomas Petazzoni (2):
  PCI: Introduce PCI bridge emulated config space common logic
  PCI: mvebu: Convert to PCI emulated bridge config space

Zachary Zhang (1):
  PCI: aardvark: Implement emulated root PCI bridge config space

 drivers/pci/Kconfig                   |   3 +
 drivers/pci/Makefile                  |   1 +
 drivers/pci/controller/Kconfig        |   2 +
 drivers/pci/controller/pci-aardvark.c | 129 ++++++++++-
 drivers/pci/controller/pci-mvebu.c    | 375 +++++++++----------------------
 drivers/pci/pci-bridge-emul.c         | 408 ++++++++++++++++++++++++++++++++++
 drivers/pci/pci-bridge-emul.h         | 124 +++++++++++
 7 files changed, 770 insertions(+), 272 deletions(-)
 create mode 100644 drivers/pci/pci-bridge-emul.c
 create mode 100644 drivers/pci/pci-bridge-emul.h

-- 
2.14.4

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCHv2 1/3] PCI: Introduce PCI bridge emulated config space common logic
  2018-09-12 15:48 ` Thomas Petazzoni
@ 2018-09-12 15:48   ` Thomas Petazzoni
  -1 siblings, 0 replies; 24+ messages in thread
From: Thomas Petazzoni @ 2018-09-12 15:48 UTC (permalink / raw)
  To: Bjorn Helgaas, Lorenzo Pieralisi, Russell King
  Cc: linux-pci, linux-arm-kernel, Gregory Clement, Miquèl Raynal,
	Maxime Chevallier, Antoine Tenart, Nadav Haklai,
	Thomas Petazzoni

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>
---
 drivers/pci/Kconfig           |   3 +
 drivers/pci/Makefile          |   1 +
 drivers/pci/pci-bridge-emul.c | 408 ++++++++++++++++++++++++++++++++++++++++++
 drivers/pci/pci-bridge-emul.h | 124 +++++++++++++
 4 files changed, 536 insertions(+)
 create mode 100644 drivers/pci/pci-bridge-emul.c
 create mode 100644 drivers/pci/pci-bridge-emul.h

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
 config PCI_LOCKLESS_CONFIG
 	bool
 
+config PCI_BRIDGE_EMUL
+	bool
+
 config PCI_IOV
 	bool "PCI IOV support"
 	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/
 obj-$(CONFIG_PCI_MSI)		+= msi.o
 obj-$(CONFIG_PCI_ATS)		+= ats.o
 obj-$(CONFIG_PCI_IOV)		+= iov.o
+obj-$(CONFIG_PCI_BRIDGE_EMUL)	+= pci-bridge-emul.o
 obj-$(CONFIG_ACPI)		+= pci-acpi.o
 obj-$(CONFIG_PCI_LABEL)		+= pci-label.o
 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..a8299ff3b16c
--- /dev/null
+++ b/drivers/pci/pci-bridge-emul.c
@@ -0,0 +1,408 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell
+ *
+ * Author: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
+ *
+ * This file helps PCI controller drivers implement a fake root port
+ * PCI bridge when the HW doesn't provide such a root port PCI
+ * bridge.
+ *
+ * It emulates a PCI bridge by providing a fake PCI configuration
+ * space (and optionally a PCIe capability configuration space) in
+ * memory. By default the read/write operations simply read and update
+ * this fake configuration space in memory. However, PCI controller
+ * drivers can provide through the 'struct pci_sw_bridge_ops'
+ * structure a set of operations to override or complement this
+ * default behavior.
+ */
+
+#include <linux/pci.h>
+#include "pci-bridge-emul.h"
+
+#define PCI_BRIDGE_CONF_END	(PCI_BRIDGE_CONTROL + 2)
+#define PCI_CAP_PCIE_START	PCI_BRIDGE_CONF_END
+#define PCI_CAP_PCIE_END	(PCI_CAP_PCIE_START + PCI_EXP_SLTSTA2 + 2)
+
+/*
+ * Initialize a pci_bridge_emul structure to represent a fake PCI
+ * bridge configuration space. The caller needs to have initialized
+ * the PCI configuration space with whatever values make sense
+ * (typically at least vendor, device, revision), the ->ops pointer,
+ * and optionally ->data and ->has_pcie.
+ */
+void pci_bridge_emul_init(struct pci_bridge_emul *bridge)
+{
+	bridge->conf.class_revision |= PCI_CLASS_BRIDGE_PCI << 16;
+	bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE;
+	bridge->conf.cache_line_size = 0x10;
+	bridge->conf.status = PCI_STATUS_CAP_LIST;
+
+	if (bridge->has_pcie) {
+		bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START;
+		bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP;
+		/* Set PCIe v2, root port, slot support */
+		bridge->pcie_conf.cap = PCI_EXP_TYPE_ROOT_PORT << 4 | 2 |
+			PCI_EXP_FLAGS_SLOT;
+	}
+}
+
+struct pci_bridge_reg_behavior {
+	/* Read-only bits */
+	u32 ro;
+
+	/* Read-write bits */
+	u32 rw;
+
+	/* Write-1-to-clear bits */
+	u32 w1c;
+
+	/* Reserved bits (hardwired to 0) */
+	u32 rsvd;
+};
+
+const static struct pci_bridge_reg_behavior pci_regs_behavior[] = {
+	[PCI_VENDOR_ID / 4] = { .ro = ~0 },
+	[PCI_COMMAND / 4] = {
+		.rw = (PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+		       PCI_COMMAND_MASTER | PCI_COMMAND_PARITY |
+		       PCI_COMMAND_SERR),
+		.ro = ((PCI_COMMAND_SPECIAL | PCI_COMMAND_INVALIDATE |
+			PCI_COMMAND_VGA_PALETTE | PCI_COMMAND_WAIT |
+			PCI_COMMAND_FAST_BACK) |
+		       (PCI_STATUS_CAP_LIST | PCI_STATUS_66MHZ |
+			PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MASK) << 16),
+		.rsvd = GENMASK(15, 10) | ((BIT(6) | GENMASK(3, 0)) << 16),
+		.w1c = (PCI_STATUS_PARITY |
+			PCI_STATUS_SIG_TARGET_ABORT |
+			PCI_STATUS_REC_TARGET_ABORT |
+			PCI_STATUS_REC_MASTER_ABORT |
+			PCI_STATUS_SIG_SYSTEM_ERROR |
+			PCI_STATUS_DETECTED_PARITY) << 16,
+	},
+	[PCI_CLASS_REVISION / 4] = { .ro = ~0 },
+
+	/*
+	 * Cache Line Size register: implement as read-only, we do not
+	 * pretend implementing "Memory Write and Invalidate"
+	 * transactions"
+	 *
+	 * Latency Timer Register: implemented as read-only, as "A
+	 * bridge that is not capable of a burst transfer of more than
+	 * two data phases on its primary interface is permitted to
+	 * hardwire the Latency Timer to a value of 16 or less"
+	 *
+	 * Header Type: always read-only
+	 *
+	 * BIST register: implemented as read-only, as "A bridge that
+	 * does not support BIST must implement this register as a
+	 * read-only register that returns 0 when read"
+	 */
+	[PCI_CACHE_LINE_SIZE / 4] = { .ro = ~0 },
+
+	/*
+	 * Base Address registers not used must be implemented as
+	 * read-only registers that return 0 when read.
+	 */
+	[PCI_BASE_ADDRESS_0 / 4] = { .ro = ~0 },
+	[PCI_BASE_ADDRESS_1 / 4] = { .ro = ~0 },
+
+	[PCI_PRIMARY_BUS / 4] = {
+		/* Primary, secondary and subordinate bus are RW */
+		.rw = GENMASK(24, 0),
+		/* Secondary latency is read-only */
+		.ro = GENMASK(31, 24),
+	},
+
+	[PCI_IO_BASE / 4] = {
+		/* The high four bits of I/O base/limit are RW */
+		.rw = (GENMASK(15, 12) | GENMASK(7, 4)),
+
+		/* The low four bits of I/O base/limit are RO */
+		.ro = (((PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK |
+			 PCI_STATUS_DEVSEL_MASK) << 16) |
+		       GENMASK(11, 8) | GENMASK(3, 0)),
+
+		.w1c = (PCI_STATUS_PARITY |
+			PCI_STATUS_SIG_TARGET_ABORT |
+			PCI_STATUS_REC_TARGET_ABORT |
+			PCI_STATUS_REC_MASTER_ABORT |
+			PCI_STATUS_SIG_SYSTEM_ERROR |
+			PCI_STATUS_DETECTED_PARITY) << 16,
+
+		.rsvd = ((BIT(6) | GENMASK(4, 0)) << 16),
+	},
+
+	[PCI_MEMORY_BASE / 4] = {
+		/* The high 12-bits of mem base/limit are RW */
+		.rw = GENMASK(31, 20) | GENMASK(15, 4),
+
+		/* The low four bits of mem base/limit are RO */
+		.ro = GENMASK(19, 16) | GENMASK(3, 0),
+	},
+
+	[PCI_PREF_MEMORY_BASE / 4] = {
+		/* The high 12-bits of pref mem base/limit are RW */
+		.rw = GENMASK(31, 20) | GENMASK(15, 4),
+
+		/* The low four bits of pref mem base/limit are RO */
+		.ro = GENMASK(19, 16) | GENMASK(3, 0),
+	},
+
+	[PCI_PREF_BASE_UPPER32 / 4] = {
+		.rw = ~0,
+	},
+
+	[PCI_PREF_LIMIT_UPPER32 / 4] = {
+		.rw = ~0,
+	},
+
+	[PCI_IO_BASE_UPPER16 / 4] = {
+		.rw = ~0,
+	},
+
+	[PCI_CAPABILITY_LIST / 4] = {
+		.ro = GENMASK(7, 0),
+		.rsvd = GENMASK(31, 8),
+	},
+
+	[PCI_ROM_ADDRESS1 / 4] = {
+		.rw = GENMASK(31, 11) | BIT(0),
+		.rsvd = GENMASK(10, 1),
+	},
+
+	/*
+	 * Interrupt line (bits 7:0) are RW, interrupt pin (bits 15:8)
+	 * are RO, and bridge control (31:16) are a mix of RW, RO,
+	 * reserved and W1C bits
+	 */
+	[PCI_INTERRUPT_LINE / 4] = {
+		/* Interrupt line is RW */
+		.rw = (GENMASK(7, 0) |
+		       ((PCI_BRIDGE_CTL_PARITY |
+			 PCI_BRIDGE_CTL_SERR |
+			 PCI_BRIDGE_CTL_ISA |
+			 PCI_BRIDGE_CTL_VGA |
+			 PCI_BRIDGE_CTL_MASTER_ABORT |
+			 PCI_BRIDGE_CTL_BUS_RESET |
+			 BIT(8) | BIT(9) | BIT(11)) << 16)),
+
+		/* Interrupt pin is RO */
+		.ro = (GENMASK(15, 8) | ((PCI_BRIDGE_CTL_FAST_BACK) << 16)),
+
+		.w1c = BIT(10) << 16,
+
+		.rsvd = (GENMASK(15, 12) | BIT(4)) << 16,
+	},
+};
+
+const static struct pci_bridge_reg_behavior pcie_cap_regs_behavior[] = {
+	[PCI_CAP_LIST_ID / 4] = {
+		/*
+		 * Capability ID, Next Capability Pointer and
+		 * Capabilities register are all read-only.
+		 */
+		.ro = ~0,
+	},
+
+	[PCI_EXP_DEVCAP / 4] = {
+		.ro = ~0,
+	},
+
+	[PCI_EXP_DEVCTL / 4] = {
+		/* Device control register is RW */
+		.rw = GENMASK(15, 0),
+
+		/*
+		 * Device status register has 4 bits W1C, then 2 bits
+		 * RO, the rest is reserved
+		 */
+		.w1c = GENMASK(19, 16),
+		.ro = GENMASK(20, 19),
+		.rsvd = GENMASK(31, 21),
+	},
+
+	[PCI_EXP_LNKCAP / 4] = {
+		/* All bits are RO, except bit 23 which is reserved */
+		.ro = lower_32_bits(~BIT(23)),
+		.rsvd = BIT(23),
+	},
+
+	[PCI_EXP_LNKCTL / 4] = {
+		/*
+		 * Link control has bits [1:0] and [11:3] RW, the
+		 * other bits are reserved.
+		 * Link status has bits [13:0] RO, and bits [14:15]
+		 * W1C.
+		 */
+		.rw = GENMASK(11, 3) | GENMASK(1, 0),
+		.ro = GENMASK(13, 0) << 16,
+		.w1c = GENMASK(15, 14) << 16,
+		.rsvd = GENMASK(15, 12) | BIT(2),
+	},
+
+	[PCI_EXP_SLTCAP / 4] = {
+		.ro = ~0,
+	},
+
+	[PCI_EXP_SLTCTL / 4] = {
+		/*
+		 * Slot control has bits [12:0] RW, the rest is
+		 * reserved.
+		 *
+		 * Slot status has a mix of W1C and RO bits, as well
+		 * as reserved bits.
+		 */
+		.rw = GENMASK(12, 0),
+		.w1c = (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
+			PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC |
+			PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC) << 16,
+		.ro = (PCI_EXP_SLTSTA_MRLSS | PCI_EXP_SLTSTA_PDS |
+		       PCI_EXP_SLTSTA_EIS) << 16,
+		.rsvd = GENMASK(15, 12) | (GENMASK(15, 9) << 16),
+	},
+
+	[PCI_EXP_RTCTL / 4] = {
+		/*
+		 * Root control has bits [4:0] RW, the rest is
+		 * reserved.
+		 *
+		 * Root status has bit 0 RO, the rest is reserved.
+		 */
+		.rw = (PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE |
+		       PCI_EXP_RTCTL_SEFEE | PCI_EXP_RTCTL_PMEIE |
+		       PCI_EXP_RTCTL_CRSSVE),
+		.ro = PCI_EXP_RTCAP_CRSVIS << 16,
+		.rsvd = GENMASK(15, 5) | (GENMASK(15, 1) << 16),
+	},
+
+	[PCI_EXP_RTSTA / 4] = {
+		.ro = GENMASK(15, 0) | PCI_EXP_RTSTA_PENDING,
+		.w1c = PCI_EXP_RTSTA_PME,
+		.rsvd = GENMASK(31, 18),
+	},
+};
+
+/*
+ * 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_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
+			      int size, u32 *value)
+{
+	int ret;
+	int reg = where & ~3;
+	pci_bridge_emul_read_status_t (*read_op)(struct pci_bridge_emul *bridge,
+						 int reg, u32 *value);
+	u32 *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) {
+		*value = 0;
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) {
+		reg -= PCI_CAP_PCIE_START;
+		read_op = bridge->ops->read_pcie;
+		cfgspace = (u32 *) &bridge->pcie_conf;
+		behavior = pcie_cap_regs_behavior;
+	} else {
+		read_op = bridge->ops->read_base;
+		cfgspace = (u32 *) &bridge->conf;
+		behavior = pci_regs_behavior;
+	}
+
+	if (read_op)
+		ret = read_op(bridge, reg, value);
+	else
+		ret = PCI_BRIDGE_EMUL_NOT_HANDLED;
+
+	if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED)
+		*value = cfgspace[reg / 4];
+
+	/*
+	 * Make sure we never return any reserved bit with a value
+	 * different from 0.
+	 */
+	*value &= ~behavior[reg / 4].rsvd;
+
+	if (size == 1)
+		*value = (*value >> (8 * (where & 3))) & 0xff;
+	else if (size == 2)
+		*value = (*value >> (8 * (where & 3))) & 0xffff;
+	else if (size != 4)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+/*
+ * Should be called by the PCI controller driver when writing the PCI
+ * configuration space of the fake bridge. It will call back the
+ * ->ops->write_base or ->ops->write_pcie operations.
+ */
+int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
+			       int size, u32 value)
+{
+	int reg = where & ~3;
+	int mask, ret, old, new, shift;
+	void (*write_op)(struct pci_bridge_emul *bridge, int reg,
+			 u32 old, u32 new, u32 mask);
+	u32 *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)
+		mask = 0xffffffff;
+	else if (size == 2)
+		mask = 0xffff << shift;
+	else if (size == 1)
+		mask = 0xff << shift;
+	else
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	ret = pci_bridge_emul_conf_read(bridge, reg, 4, &old);
+	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 = (u32 *) &bridge->pcie_conf;
+		behavior = pcie_cap_regs_behavior;
+	} else {
+		write_op = bridge->ops->write_base;
+		cfgspace = (u32 *) &bridge->conf;
+		behavior = pci_regs_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));
+
+	cfgspace[reg / 4] = new;
+
+	if (write_op)
+		write_op(bridge, reg, old, new, mask);
+
+	return PCIBIOS_SUCCESSFUL;
+}
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 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PCI_BRIDGE_EMUL_H__
+#define __PCI_BRIDGE_EMUL_H__
+
+#include <linux/kernel.h>
+
+/* PCI configuration space of a PCI-to-PCI bridge. */
+struct pci_bridge_emul_conf {
+	u16 vendor;
+	u16 device;
+	u16 command;
+	u16 status;
+	u32 class_revision;
+	u8 cache_line_size;
+	u8 latency_timer;
+	u8 header_type;
+	u8 bist;
+	u32 bar[2];
+	u8 primary_bus;
+	u8 secondary_bus;
+	u8 subordinate_bus;
+	u8 secondary_latency_timer;
+	u8 iobase;
+	u8 iolimit;
+	u16 secondary_status;
+	u16 membase;
+	u16 memlimit;
+	u16 pref_mem_base;
+	u16 pref_mem_limit;
+	u32 prefbaseupper;
+	u32 preflimitupper;
+	u16 iobaseupper;
+	u16 iolimitupper;
+	u8 capabilities_pointer;
+	u8 reserve[3];
+	u32 romaddr;
+	u8 intline;
+	u8 intpin;
+	u16 bridgectrl;
+};
+
+/* PCI configuration space of the PCIe capabilities */
+struct pci_bridge_emul_pcie_conf {
+	u8 cap_id;
+	u8 next;
+	u16 cap;
+	u32 devcap;
+	u16 devctl;
+	u16 devsta;
+	u32 lnkcap;
+	u16 lnkctl;
+	u16 lnksta;
+	u32 slotcap;
+	u16 slotctl;
+	u16 slotsta;
+	u16 rootctl;
+	u16 rsvd;
+	u32 rootsta;
+	u32 devcap2;
+	u16 devctl2;
+	u16 devsta2;
+	u32 lnkcap2;
+	u16 lnkctl2;
+	u16 lnksta2;
+	u32 slotcap2;
+	u16 slotctl2;
+	u16 slotsta2;
+};
+
+struct pci_bridge_emul;
+
+typedef enum { PCI_BRIDGE_EMUL_HANDLED,
+	       PCI_BRIDGE_EMUL_NOT_HANDLED } pci_bridge_emul_read_status_t;
+
+struct pci_bridge_emul_ops {
+	/*
+	 * Called when reading from the regular PCI bridge
+	 * configuration space. Return PCI_BRIDGE_EMUL_HANDLED when the
+	 * operation has handled the read operation and filled in the
+	 * *value, or PCI_BRIDGE_EMUL_NOT_HANDLED when the read should
+	 * be emulated by the common code by reading from the
+	 * in-memory copy of the configuration space.
+	 */
+	pci_bridge_emul_read_status_t (*read_base)(struct pci_bridge_emul *bridge,
+						   int reg, u32 *value);
+
+	/*
+	 * Same as ->read_base(), except it is for reading from the
+	 * PCIe capability configuration space.
+	 */
+	pci_bridge_emul_read_status_t (*read_pcie)(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
+	 * written, and mask indicates which parts of the value are
+	 * being changed.
+	 */
+	void (*write_base)(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 capability configuration space.
+	 */
+	void (*write_pcie)(struct pci_bridge_emul *bridge, int reg,
+			   u32 old, u32 new, u32 mask);
+};
+
+struct pci_bridge_emul {
+	struct pci_bridge_emul_conf conf;
+	struct pci_bridge_emul_pcie_conf pcie_conf;
+	struct pci_bridge_emul_ops *ops;
+	void *data;
+	bool has_pcie;
+};
+
+void pci_bridge_emul_init(struct pci_bridge_emul *bridge);
+int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
+			      int size, u32 *value);
+int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
+			       int size, u32 value);
+
+#endif /* __PCI_BRIDGE_EMUL_H__ */
-- 
2.14.4

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCHv2 1/3] PCI: Introduce PCI bridge emulated config space common logic
@ 2018-09-12 15:48   ` Thomas Petazzoni
  0 siblings, 0 replies; 24+ messages in thread
From: Thomas Petazzoni @ 2018-09-12 15:48 UTC (permalink / raw)
  To: linux-arm-kernel

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>
---
 drivers/pci/Kconfig           |   3 +
 drivers/pci/Makefile          |   1 +
 drivers/pci/pci-bridge-emul.c | 408 ++++++++++++++++++++++++++++++++++++++++++
 drivers/pci/pci-bridge-emul.h | 124 +++++++++++++
 4 files changed, 536 insertions(+)
 create mode 100644 drivers/pci/pci-bridge-emul.c
 create mode 100644 drivers/pci/pci-bridge-emul.h

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
 config PCI_LOCKLESS_CONFIG
 	bool
 
+config PCI_BRIDGE_EMUL
+	bool
+
 config PCI_IOV
 	bool "PCI IOV support"
 	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/
 obj-$(CONFIG_PCI_MSI)		+= msi.o
 obj-$(CONFIG_PCI_ATS)		+= ats.o
 obj-$(CONFIG_PCI_IOV)		+= iov.o
+obj-$(CONFIG_PCI_BRIDGE_EMUL)	+= pci-bridge-emul.o
 obj-$(CONFIG_ACPI)		+= pci-acpi.o
 obj-$(CONFIG_PCI_LABEL)		+= pci-label.o
 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..a8299ff3b16c
--- /dev/null
+++ b/drivers/pci/pci-bridge-emul.c
@@ -0,0 +1,408 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell
+ *
+ * Author: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
+ *
+ * This file helps PCI controller drivers implement a fake root port
+ * PCI bridge when the HW doesn't provide such a root port PCI
+ * bridge.
+ *
+ * It emulates a PCI bridge by providing a fake PCI configuration
+ * space (and optionally a PCIe capability configuration space) in
+ * memory. By default the read/write operations simply read and update
+ * this fake configuration space in memory. However, PCI controller
+ * drivers can provide through the 'struct pci_sw_bridge_ops'
+ * structure a set of operations to override or complement this
+ * default behavior.
+ */
+
+#include <linux/pci.h>
+#include "pci-bridge-emul.h"
+
+#define PCI_BRIDGE_CONF_END	(PCI_BRIDGE_CONTROL + 2)
+#define PCI_CAP_PCIE_START	PCI_BRIDGE_CONF_END
+#define PCI_CAP_PCIE_END	(PCI_CAP_PCIE_START + PCI_EXP_SLTSTA2 + 2)
+
+/*
+ * Initialize a pci_bridge_emul structure to represent a fake PCI
+ * bridge configuration space. The caller needs to have initialized
+ * the PCI configuration space with whatever values make sense
+ * (typically at least vendor, device, revision), the ->ops pointer,
+ * and optionally ->data and ->has_pcie.
+ */
+void pci_bridge_emul_init(struct pci_bridge_emul *bridge)
+{
+	bridge->conf.class_revision |= PCI_CLASS_BRIDGE_PCI << 16;
+	bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE;
+	bridge->conf.cache_line_size = 0x10;
+	bridge->conf.status = PCI_STATUS_CAP_LIST;
+
+	if (bridge->has_pcie) {
+		bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START;
+		bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP;
+		/* Set PCIe v2, root port, slot support */
+		bridge->pcie_conf.cap = PCI_EXP_TYPE_ROOT_PORT << 4 | 2 |
+			PCI_EXP_FLAGS_SLOT;
+	}
+}
+
+struct pci_bridge_reg_behavior {
+	/* Read-only bits */
+	u32 ro;
+
+	/* Read-write bits */
+	u32 rw;
+
+	/* Write-1-to-clear bits */
+	u32 w1c;
+
+	/* Reserved bits (hardwired to 0) */
+	u32 rsvd;
+};
+
+const static struct pci_bridge_reg_behavior pci_regs_behavior[] = {
+	[PCI_VENDOR_ID / 4] = { .ro = ~0 },
+	[PCI_COMMAND / 4] = {
+		.rw = (PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+		       PCI_COMMAND_MASTER | PCI_COMMAND_PARITY |
+		       PCI_COMMAND_SERR),
+		.ro = ((PCI_COMMAND_SPECIAL | PCI_COMMAND_INVALIDATE |
+			PCI_COMMAND_VGA_PALETTE | PCI_COMMAND_WAIT |
+			PCI_COMMAND_FAST_BACK) |
+		       (PCI_STATUS_CAP_LIST | PCI_STATUS_66MHZ |
+			PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MASK) << 16),
+		.rsvd = GENMASK(15, 10) | ((BIT(6) | GENMASK(3, 0)) << 16),
+		.w1c = (PCI_STATUS_PARITY |
+			PCI_STATUS_SIG_TARGET_ABORT |
+			PCI_STATUS_REC_TARGET_ABORT |
+			PCI_STATUS_REC_MASTER_ABORT |
+			PCI_STATUS_SIG_SYSTEM_ERROR |
+			PCI_STATUS_DETECTED_PARITY) << 16,
+	},
+	[PCI_CLASS_REVISION / 4] = { .ro = ~0 },
+
+	/*
+	 * Cache Line Size register: implement as read-only, we do not
+	 * pretend implementing "Memory Write and Invalidate"
+	 * transactions"
+	 *
+	 * Latency Timer Register: implemented as read-only, as "A
+	 * bridge that is not capable of a burst transfer of more than
+	 * two data phases on its primary interface is permitted to
+	 * hardwire the Latency Timer to a value of 16 or less"
+	 *
+	 * Header Type: always read-only
+	 *
+	 * BIST register: implemented as read-only, as "A bridge that
+	 * does not support BIST must implement this register as a
+	 * read-only register that returns 0 when read"
+	 */
+	[PCI_CACHE_LINE_SIZE / 4] = { .ro = ~0 },
+
+	/*
+	 * Base Address registers not used must be implemented as
+	 * read-only registers that return 0 when read.
+	 */
+	[PCI_BASE_ADDRESS_0 / 4] = { .ro = ~0 },
+	[PCI_BASE_ADDRESS_1 / 4] = { .ro = ~0 },
+
+	[PCI_PRIMARY_BUS / 4] = {
+		/* Primary, secondary and subordinate bus are RW */
+		.rw = GENMASK(24, 0),
+		/* Secondary latency is read-only */
+		.ro = GENMASK(31, 24),
+	},
+
+	[PCI_IO_BASE / 4] = {
+		/* The high four bits of I/O base/limit are RW */
+		.rw = (GENMASK(15, 12) | GENMASK(7, 4)),
+
+		/* The low four bits of I/O base/limit are RO */
+		.ro = (((PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK |
+			 PCI_STATUS_DEVSEL_MASK) << 16) |
+		       GENMASK(11, 8) | GENMASK(3, 0)),
+
+		.w1c = (PCI_STATUS_PARITY |
+			PCI_STATUS_SIG_TARGET_ABORT |
+			PCI_STATUS_REC_TARGET_ABORT |
+			PCI_STATUS_REC_MASTER_ABORT |
+			PCI_STATUS_SIG_SYSTEM_ERROR |
+			PCI_STATUS_DETECTED_PARITY) << 16,
+
+		.rsvd = ((BIT(6) | GENMASK(4, 0)) << 16),
+	},
+
+	[PCI_MEMORY_BASE / 4] = {
+		/* The high 12-bits of mem base/limit are RW */
+		.rw = GENMASK(31, 20) | GENMASK(15, 4),
+
+		/* The low four bits of mem base/limit are RO */
+		.ro = GENMASK(19, 16) | GENMASK(3, 0),
+	},
+
+	[PCI_PREF_MEMORY_BASE / 4] = {
+		/* The high 12-bits of pref mem base/limit are RW */
+		.rw = GENMASK(31, 20) | GENMASK(15, 4),
+
+		/* The low four bits of pref mem base/limit are RO */
+		.ro = GENMASK(19, 16) | GENMASK(3, 0),
+	},
+
+	[PCI_PREF_BASE_UPPER32 / 4] = {
+		.rw = ~0,
+	},
+
+	[PCI_PREF_LIMIT_UPPER32 / 4] = {
+		.rw = ~0,
+	},
+
+	[PCI_IO_BASE_UPPER16 / 4] = {
+		.rw = ~0,
+	},
+
+	[PCI_CAPABILITY_LIST / 4] = {
+		.ro = GENMASK(7, 0),
+		.rsvd = GENMASK(31, 8),
+	},
+
+	[PCI_ROM_ADDRESS1 / 4] = {
+		.rw = GENMASK(31, 11) | BIT(0),
+		.rsvd = GENMASK(10, 1),
+	},
+
+	/*
+	 * Interrupt line (bits 7:0) are RW, interrupt pin (bits 15:8)
+	 * are RO, and bridge control (31:16) are a mix of RW, RO,
+	 * reserved and W1C bits
+	 */
+	[PCI_INTERRUPT_LINE / 4] = {
+		/* Interrupt line is RW */
+		.rw = (GENMASK(7, 0) |
+		       ((PCI_BRIDGE_CTL_PARITY |
+			 PCI_BRIDGE_CTL_SERR |
+			 PCI_BRIDGE_CTL_ISA |
+			 PCI_BRIDGE_CTL_VGA |
+			 PCI_BRIDGE_CTL_MASTER_ABORT |
+			 PCI_BRIDGE_CTL_BUS_RESET |
+			 BIT(8) | BIT(9) | BIT(11)) << 16)),
+
+		/* Interrupt pin is RO */
+		.ro = (GENMASK(15, 8) | ((PCI_BRIDGE_CTL_FAST_BACK) << 16)),
+
+		.w1c = BIT(10) << 16,
+
+		.rsvd = (GENMASK(15, 12) | BIT(4)) << 16,
+	},
+};
+
+const static struct pci_bridge_reg_behavior pcie_cap_regs_behavior[] = {
+	[PCI_CAP_LIST_ID / 4] = {
+		/*
+		 * Capability ID, Next Capability Pointer and
+		 * Capabilities register are all read-only.
+		 */
+		.ro = ~0,
+	},
+
+	[PCI_EXP_DEVCAP / 4] = {
+		.ro = ~0,
+	},
+
+	[PCI_EXP_DEVCTL / 4] = {
+		/* Device control register is RW */
+		.rw = GENMASK(15, 0),
+
+		/*
+		 * Device status register has 4 bits W1C, then 2 bits
+		 * RO, the rest is reserved
+		 */
+		.w1c = GENMASK(19, 16),
+		.ro = GENMASK(20, 19),
+		.rsvd = GENMASK(31, 21),
+	},
+
+	[PCI_EXP_LNKCAP / 4] = {
+		/* All bits are RO, except bit 23 which is reserved */
+		.ro = lower_32_bits(~BIT(23)),
+		.rsvd = BIT(23),
+	},
+
+	[PCI_EXP_LNKCTL / 4] = {
+		/*
+		 * Link control has bits [1:0] and [11:3] RW, the
+		 * other bits are reserved.
+		 * Link status has bits [13:0] RO, and bits [14:15]
+		 * W1C.
+		 */
+		.rw = GENMASK(11, 3) | GENMASK(1, 0),
+		.ro = GENMASK(13, 0) << 16,
+		.w1c = GENMASK(15, 14) << 16,
+		.rsvd = GENMASK(15, 12) | BIT(2),
+	},
+
+	[PCI_EXP_SLTCAP / 4] = {
+		.ro = ~0,
+	},
+
+	[PCI_EXP_SLTCTL / 4] = {
+		/*
+		 * Slot control has bits [12:0] RW, the rest is
+		 * reserved.
+		 *
+		 * Slot status has a mix of W1C and RO bits, as well
+		 * as reserved bits.
+		 */
+		.rw = GENMASK(12, 0),
+		.w1c = (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
+			PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC |
+			PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC) << 16,
+		.ro = (PCI_EXP_SLTSTA_MRLSS | PCI_EXP_SLTSTA_PDS |
+		       PCI_EXP_SLTSTA_EIS) << 16,
+		.rsvd = GENMASK(15, 12) | (GENMASK(15, 9) << 16),
+	},
+
+	[PCI_EXP_RTCTL / 4] = {
+		/*
+		 * Root control has bits [4:0] RW, the rest is
+		 * reserved.
+		 *
+		 * Root status has bit 0 RO, the rest is reserved.
+		 */
+		.rw = (PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE |
+		       PCI_EXP_RTCTL_SEFEE | PCI_EXP_RTCTL_PMEIE |
+		       PCI_EXP_RTCTL_CRSSVE),
+		.ro = PCI_EXP_RTCAP_CRSVIS << 16,
+		.rsvd = GENMASK(15, 5) | (GENMASK(15, 1) << 16),
+	},
+
+	[PCI_EXP_RTSTA / 4] = {
+		.ro = GENMASK(15, 0) | PCI_EXP_RTSTA_PENDING,
+		.w1c = PCI_EXP_RTSTA_PME,
+		.rsvd = GENMASK(31, 18),
+	},
+};
+
+/*
+ * 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_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
+			      int size, u32 *value)
+{
+	int ret;
+	int reg = where & ~3;
+	pci_bridge_emul_read_status_t (*read_op)(struct pci_bridge_emul *bridge,
+						 int reg, u32 *value);
+	u32 *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) {
+		*value = 0;
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) {
+		reg -= PCI_CAP_PCIE_START;
+		read_op = bridge->ops->read_pcie;
+		cfgspace = (u32 *) &bridge->pcie_conf;
+		behavior = pcie_cap_regs_behavior;
+	} else {
+		read_op = bridge->ops->read_base;
+		cfgspace = (u32 *) &bridge->conf;
+		behavior = pci_regs_behavior;
+	}
+
+	if (read_op)
+		ret = read_op(bridge, reg, value);
+	else
+		ret = PCI_BRIDGE_EMUL_NOT_HANDLED;
+
+	if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED)
+		*value = cfgspace[reg / 4];
+
+	/*
+	 * Make sure we never return any reserved bit with a value
+	 * different from 0.
+	 */
+	*value &= ~behavior[reg / 4].rsvd;
+
+	if (size == 1)
+		*value = (*value >> (8 * (where & 3))) & 0xff;
+	else if (size == 2)
+		*value = (*value >> (8 * (where & 3))) & 0xffff;
+	else if (size != 4)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+/*
+ * Should be called by the PCI controller driver when writing the PCI
+ * configuration space of the fake bridge. It will call back the
+ * ->ops->write_base or ->ops->write_pcie operations.
+ */
+int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
+			       int size, u32 value)
+{
+	int reg = where & ~3;
+	int mask, ret, old, new, shift;
+	void (*write_op)(struct pci_bridge_emul *bridge, int reg,
+			 u32 old, u32 new, u32 mask);
+	u32 *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)
+		mask = 0xffffffff;
+	else if (size == 2)
+		mask = 0xffff << shift;
+	else if (size == 1)
+		mask = 0xff << shift;
+	else
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	ret = pci_bridge_emul_conf_read(bridge, reg, 4, &old);
+	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 = (u32 *) &bridge->pcie_conf;
+		behavior = pcie_cap_regs_behavior;
+	} else {
+		write_op = bridge->ops->write_base;
+		cfgspace = (u32 *) &bridge->conf;
+		behavior = pci_regs_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));
+
+	cfgspace[reg / 4] = new;
+
+	if (write_op)
+		write_op(bridge, reg, old, new, mask);
+
+	return PCIBIOS_SUCCESSFUL;
+}
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 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PCI_BRIDGE_EMUL_H__
+#define __PCI_BRIDGE_EMUL_H__
+
+#include <linux/kernel.h>
+
+/* PCI configuration space of a PCI-to-PCI bridge. */
+struct pci_bridge_emul_conf {
+	u16 vendor;
+	u16 device;
+	u16 command;
+	u16 status;
+	u32 class_revision;
+	u8 cache_line_size;
+	u8 latency_timer;
+	u8 header_type;
+	u8 bist;
+	u32 bar[2];
+	u8 primary_bus;
+	u8 secondary_bus;
+	u8 subordinate_bus;
+	u8 secondary_latency_timer;
+	u8 iobase;
+	u8 iolimit;
+	u16 secondary_status;
+	u16 membase;
+	u16 memlimit;
+	u16 pref_mem_base;
+	u16 pref_mem_limit;
+	u32 prefbaseupper;
+	u32 preflimitupper;
+	u16 iobaseupper;
+	u16 iolimitupper;
+	u8 capabilities_pointer;
+	u8 reserve[3];
+	u32 romaddr;
+	u8 intline;
+	u8 intpin;
+	u16 bridgectrl;
+};
+
+/* PCI configuration space of the PCIe capabilities */
+struct pci_bridge_emul_pcie_conf {
+	u8 cap_id;
+	u8 next;
+	u16 cap;
+	u32 devcap;
+	u16 devctl;
+	u16 devsta;
+	u32 lnkcap;
+	u16 lnkctl;
+	u16 lnksta;
+	u32 slotcap;
+	u16 slotctl;
+	u16 slotsta;
+	u16 rootctl;
+	u16 rsvd;
+	u32 rootsta;
+	u32 devcap2;
+	u16 devctl2;
+	u16 devsta2;
+	u32 lnkcap2;
+	u16 lnkctl2;
+	u16 lnksta2;
+	u32 slotcap2;
+	u16 slotctl2;
+	u16 slotsta2;
+};
+
+struct pci_bridge_emul;
+
+typedef enum { PCI_BRIDGE_EMUL_HANDLED,
+	       PCI_BRIDGE_EMUL_NOT_HANDLED } pci_bridge_emul_read_status_t;
+
+struct pci_bridge_emul_ops {
+	/*
+	 * Called when reading from the regular PCI bridge
+	 * configuration space. Return PCI_BRIDGE_EMUL_HANDLED when the
+	 * operation has handled the read operation and filled in the
+	 * *value, or PCI_BRIDGE_EMUL_NOT_HANDLED when the read should
+	 * be emulated by the common code by reading from the
+	 * in-memory copy of the configuration space.
+	 */
+	pci_bridge_emul_read_status_t (*read_base)(struct pci_bridge_emul *bridge,
+						   int reg, u32 *value);
+
+	/*
+	 * Same as ->read_base(), except it is for reading from the
+	 * PCIe capability configuration space.
+	 */
+	pci_bridge_emul_read_status_t (*read_pcie)(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
+	 * written, and mask indicates which parts of the value are
+	 * being changed.
+	 */
+	void (*write_base)(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 capability configuration space.
+	 */
+	void (*write_pcie)(struct pci_bridge_emul *bridge, int reg,
+			   u32 old, u32 new, u32 mask);
+};
+
+struct pci_bridge_emul {
+	struct pci_bridge_emul_conf conf;
+	struct pci_bridge_emul_pcie_conf pcie_conf;
+	struct pci_bridge_emul_ops *ops;
+	void *data;
+	bool has_pcie;
+};
+
+void pci_bridge_emul_init(struct pci_bridge_emul *bridge);
+int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
+			      int size, u32 *value);
+int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
+			       int size, u32 value);
+
+#endif /* __PCI_BRIDGE_EMUL_H__ */
-- 
2.14.4

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCHv2 2/3] PCI: mvebu: Convert to PCI emulated bridge config space
  2018-09-12 15:48 ` Thomas Petazzoni
@ 2018-09-12 15:48   ` Thomas Petazzoni
  -1 siblings, 0 replies; 24+ messages in thread
From: Thomas Petazzoni @ 2018-09-12 15:48 UTC (permalink / raw)
  To: Bjorn Helgaas, Lorenzo Pieralisi, Russell King
  Cc: linux-pci, linux-arm-kernel, Gregory Clement, Miquèl Raynal,
	Maxime Chevallier, Antoine Tenart, Nadav Haklai,
	Thomas Petazzoni

This commit convers the pci-mvebu driver to use the recently
introduced pci-bridge-emul logic, that helps emulating a root port PCI
bridge configuration space.

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 | 375 +++++++++++--------------------------
 2 files changed, 107 insertions(+), 269 deletions(-)

diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
index 028b287466fb..6d0f9930be7f 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -9,6 +9,7 @@ config PCI_MVEBU
 	depends on MVEBU_MBUS
 	depends on ARM
 	depends on OF
+	select PCI_BRIDGE_EMUL
 
 config PCI_AARDVARK
 	bool "Aardvark PCIe controller"
diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
index 50eb0729385b..f97f48cf2bf6 100644
--- a/drivers/pci/controller/pci-mvebu.c
+++ b/drivers/pci/controller/pci-mvebu.c
@@ -22,6 +22,7 @@
 #include <linux/of_platform.h>
 
 #include "../pci.h"
+#include "../pci-bridge-emul.h"
 
 /*
  * PCIe unit register offsets.
@@ -63,61 +64,6 @@
 #define PCIE_DEBUG_CTRL         0x1a60
 #define  PCIE_DEBUG_SOFT_RESET		BIT(20)
 
-enum {
-	PCISWCAP = PCI_BRIDGE_CONTROL + 2,
-	PCISWCAP_EXP_LIST_ID	= PCISWCAP + PCI_CAP_LIST_ID,
-	PCISWCAP_EXP_DEVCAP	= PCISWCAP + PCI_EXP_DEVCAP,
-	PCISWCAP_EXP_DEVCTL	= PCISWCAP + PCI_EXP_DEVCTL,
-	PCISWCAP_EXP_LNKCAP	= PCISWCAP + PCI_EXP_LNKCAP,
-	PCISWCAP_EXP_LNKCTL	= PCISWCAP + PCI_EXP_LNKCTL,
-	PCISWCAP_EXP_SLTCAP	= PCISWCAP + PCI_EXP_SLTCAP,
-	PCISWCAP_EXP_SLTCTL	= PCISWCAP + PCI_EXP_SLTCTL,
-	PCISWCAP_EXP_RTCTL	= PCISWCAP + PCI_EXP_RTCTL,
-	PCISWCAP_EXP_RTSTA	= PCISWCAP + PCI_EXP_RTSTA,
-	PCISWCAP_EXP_DEVCAP2	= PCISWCAP + PCI_EXP_DEVCAP2,
-	PCISWCAP_EXP_DEVCTL2	= PCISWCAP + PCI_EXP_DEVCTL2,
-	PCISWCAP_EXP_LNKCAP2	= PCISWCAP + PCI_EXP_LNKCAP2,
-	PCISWCAP_EXP_LNKCTL2	= PCISWCAP + PCI_EXP_LNKCTL2,
-	PCISWCAP_EXP_SLTCAP2	= PCISWCAP + PCI_EXP_SLTCAP2,
-	PCISWCAP_EXP_SLTCTL2	= PCISWCAP + PCI_EXP_SLTCTL2,
-};
-
-/* PCI configuration space of a PCI-to-PCI bridge */
-struct mvebu_sw_pci_bridge {
-	u16 vendor;
-	u16 device;
-	u16 command;
-	u16 status;
-	u16 class;
-	u8 interface;
-	u8 revision;
-	u8 bist;
-	u8 header_type;
-	u8 latency_timer;
-	u8 cache_line_size;
-	u32 bar[2];
-	u8 primary_bus;
-	u8 secondary_bus;
-	u8 subordinate_bus;
-	u8 secondary_latency_timer;
-	u8 iobase;
-	u8 iolimit;
-	u16 secondary_status;
-	u16 membase;
-	u16 memlimit;
-	u16 iobaseupper;
-	u16 iolimitupper;
-	u32 romaddr;
-	u8 intline;
-	u8 intpin;
-	u16 bridgectrl;
-
-	/* PCI express capability */
-	u32 pcie_sltcap;
-	u16 pcie_devctl;
-	u16 pcie_rtctl;
-};
-
 struct mvebu_pcie_port;
 
 /* Structure representing all PCIe interfaces */
@@ -153,7 +99,7 @@ struct mvebu_pcie_port {
 	struct clk *clk;
 	struct gpio_desc *reset_gpio;
 	char *reset_name;
-	struct mvebu_sw_pci_bridge bridge;
+	struct pci_bridge_emul bridge;
 	struct device_node *dn;
 	struct mvebu_pcie *pcie;
 	struct mvebu_pcie_window memwin;
@@ -415,11 +361,12 @@ static void mvebu_pcie_set_window(struct mvebu_pcie_port *port,
 static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
 {
 	struct mvebu_pcie_window desired = {};
+	struct pci_bridge_emul_conf *conf = &port->bridge.conf;
 
 	/* Are the new iobase/iolimit values invalid? */
-	if (port->bridge.iolimit < port->bridge.iobase ||
-	    port->bridge.iolimitupper < port->bridge.iobaseupper ||
-	    !(port->bridge.command & PCI_COMMAND_IO)) {
+	if (conf->iolimit < conf->iobase ||
+	    conf->iolimitupper < conf->iobaseupper ||
+	    !(conf->command & PCI_COMMAND_IO)) {
 		mvebu_pcie_set_window(port, port->io_target, port->io_attr,
 				      &desired, &port->iowin);
 		return;
@@ -438,11 +385,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
 	 * specifications. iobase is the bus address, port->iowin_base
 	 * is the CPU address.
 	 */
-	desired.remap = ((port->bridge.iobase & 0xF0) << 8) |
-			(port->bridge.iobaseupper << 16);
+	desired.remap = ((conf->iobase & 0xF0) << 8) |
+			(conf->iobaseupper << 16);
 	desired.base = port->pcie->io.start + desired.remap;
-	desired.size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) |
-			 (port->bridge.iolimitupper << 16)) -
+	desired.size = ((0xFFF | ((conf->iolimit & 0xF0) << 8) |
+			 (conf->iolimitupper << 16)) -
 			desired.remap) +
 		       1;
 
@@ -453,10 +400,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
 static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
 {
 	struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP};
+	struct pci_bridge_emul_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;
@@ -468,130 +416,34 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
 	 * window to setup, according to the PCI-to-PCI bridge
 	 * 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);
 }
 
-/*
- * Initialize the configuration space of the PCI-to-PCI bridge
- * associated with the given PCIe interface.
- */
-static void mvebu_sw_pci_bridge_init(struct mvebu_pcie_port *port)
+static pci_bridge_emul_read_status_t
+mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
+				     int reg, u32 *value)
 {
-	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
-
-	memset(bridge, 0, sizeof(struct mvebu_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;
-}
-
-/*
- * Read the configuration space of the PCI-to-PCI bridge associated to
- * the given PCIe interface.
- */
-static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
-				  unsigned int where, int size, u32 *value)
-{
-	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
-
-	switch (where & ~3) {
-	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;
-
-	case PCI_MEMORY_BASE:
-		*value = (bridge->memlimit << 16 | bridge->membase);
-		break;
-
-	case PCI_PREF_MEMORY_BASE:
-		*value = 0;
-		break;
-
-	case PCI_IO_BASE_UPPER16:
-		*value = (bridge->iolimitupper << 16 | bridge->iobaseupper);
-		break;
-
-	case PCI_CAPABILITY_LIST:
-		*value = PCISWCAP;
-		break;
+	struct mvebu_pcie_port *port = bridge->data;
 
-	case PCI_ROM_ADDRESS1:
-		*value = 0;
-		break;
-
-	case PCI_INTERRUPT_LINE:
-		/* LINE PIN MIN_GNT MAX_LAT */
-		*value = 0;
-		break;
-
-	case PCISWCAP_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;
-
-	case PCISWCAP_EXP_DEVCAP:
+	switch (reg) {
+	case PCI_EXP_DEVCAP:
 		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP);
 		break;
 
-	case PCISWCAP_EXP_DEVCTL:
+	case PCI_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;
 
-	case PCISWCAP_EXP_LNKCAP:
+	case PCI_EXP_LNKCAP:
 		/*
 		 * PCIe requires the clock power management capability to be
 		 * hard-wired to zero for downstream ports
@@ -600,132 +452,88 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
 			 ~PCI_EXP_LNKCAP_CLKPM;
 		break;
 
-	case PCISWCAP_EXP_LNKCTL:
+	case PCI_EXP_LNKCTL:
 		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
 		break;
 
-	case PCISWCAP_EXP_SLTCAP:
-		*value = bridge->pcie_sltcap;
-		break;
-
-	case PCISWCAP_EXP_SLTCTL:
+	case PCI_EXP_SLTCTL:
 		*value = PCI_EXP_SLTSTA_PDS << 16;
 		break;
 
-	case PCISWCAP_EXP_RTCTL:
-		*value = bridge->pcie_rtctl;
-		break;
-
-	case PCISWCAP_EXP_RTSTA:
+	case PCI_EXP_RTSTA:
 		*value = mvebu_readl(port, PCIE_RC_RTSTA);
 		break;
 
-	/* PCIe requires the v2 fields to be hard-wired to zero */
-	case PCISWCAP_EXP_DEVCAP2:
-	case PCISWCAP_EXP_DEVCTL2:
-	case PCISWCAP_EXP_LNKCAP2:
-	case PCISWCAP_EXP_LNKCTL2:
-	case PCISWCAP_EXP_SLTCAP2:
-	case PCISWCAP_EXP_SLTCTL2:
 	default:
-		/*
-		 * PCI defines configuration read accesses to reserved or
-		 * unimplemented registers to read as zero and complete
-		 * normally.
-		 */
-		*value = 0;
-		return PCIBIOS_SUCCESSFUL;
+		return PCI_BRIDGE_EMUL_NOT_HANDLED;
 	}
 
-	if (size == 2)
-		*value = (*value >> (8 * (where & 3))) & 0xffff;
-	else if (size == 1)
-		*value = (*value >> (8 * (where & 3))) & 0xff;
-
-	return PCIBIOS_SUCCESSFUL;
+	return PCI_BRIDGE_EMUL_HANDLED;
 }
 
-/* Write to the PCI-to-PCI bridge configuration space */
-static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
-				     unsigned int where, int size, u32 value)
+static void
+mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge,
+				      int reg, u32 old, u32 new, u32 mask)
 {
-	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
-	u32 mask, reg;
-	int err;
-
-	if (size == 4)
-		mask = 0x0;
-	else if (size == 2)
-		mask = ~(0xffff << ((where & 3) * 8));
-	else if (size == 1)
-		mask = ~(0xff << ((where & 3) * 8));
-	else
-		return PCIBIOS_BAD_REGISTER_NUMBER;
-
-	err = mvebu_sw_pci_bridge_read(port, where & ~3, 4, &reg);
-	if (err)
-		return err;
-
-	value = (reg & mask) | value << ((where & 3) * 8);
+	struct mvebu_pcie_port *port = bridge->data;
+	struct pci_bridge_emul_conf *conf = &bridge->conf;
 
-	switch (where & ~3) {
+	switch (reg) {
 	case PCI_COMMAND:
 	{
-		u32 old = 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;
-	}
 
-	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
+		 * We 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;
+		conf->iobase |= PCI_IO_RANGE_TYPE_32;
+		conf->iolimit |= 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);
+		mvebu_pcie_set_local_bus_nr(port, conf->secondary_bus);
 		break;
 
-	case PCISWCAP_EXP_DEVCTL:
+	default:
+		break;
+	}
+}
+
+static void
+mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+				      int reg, u32 old, u32 new, u32 mask)
+{
+	struct mvebu_pcie_port *port = bridge->data;
+
+	switch (reg) {
+	case PCI_EXP_DEVCTL:
 		/*
 		 * Armada370 data says these bits must always
 		 * be zero when in root complex mode.
 		 */
-		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);
 
 		/*
 		 * If the mask is 0xffff0000, then we only want to write
@@ -733,20 +541,20 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
 		 * RW1C bits in the device status register.  Mask out the
 		 * status register bits.
 		 */
-		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;
 
-	case PCISWCAP_EXP_LNKCTL:
+	case PCI_EXP_LNKCTL:
 		/*
 		 * If we don't support CLKREQ, we must ensure that the
 		 * CLKREQ enable bit always reads zero.  Since we haven't
 		 * had this capability, and it's dependent on board wiring,
 		 * disable it for the time being.
 		 */
-		value &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
+		new &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
 
 		/*
 		 * If the mask is 0xffff0000, then we only want to write
@@ -755,21 +563,48 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
 		 * RW1C status register bits.
 		 */
 		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;
 
-	case PCISWCAP_EXP_RTSTA:
-		mvebu_writel(port, value, PCIE_RC_RTSTA);
+	case PCI_EXP_RTSTA:
+		mvebu_writel(port, new, PCIE_RC_RTSTA);
 		break;
+	}
+}
 
-	default:
-		break;
+struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = {
+	.write_base = mvebu_pci_bridge_emul_base_conf_write,
+	.read_pcie = mvebu_pci_bridge_emul_pcie_conf_read,
+	.write_pcie = mvebu_pci_bridge_emul_pcie_conf_write,
+};
+
+/*
+ * Initialize the configuration space of the PCI-to-PCI bridge
+ * associated with the given PCIe interface.
+ */
+static void mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+{
+	struct pci_bridge_emul *bridge = &port->bridge;
+
+	bridge->conf.vendor = PCI_VENDOR_ID_MARVELL;
+	bridge->conf.device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16;
+	bridge->conf.class_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;
 	}
 
-	return PCIBIOS_SUCCESSFUL;
+	bridge->has_pcie = true;
+	bridge->data = port;
+	bridge->ops = &mvebu_pci_bridge_emul_ops;
+
+	pci_bridge_emul_init(bridge);
 }
 
 static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys)
@@ -789,8 +624,8 @@ static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
 		if (bus->number == 0 && port->devfn == devfn)
 			return port;
 		if (bus->number != 0 &&
-		    bus->number >= port->bridge.secondary_bus &&
-		    bus->number <= port->bridge.subordinate_bus)
+		    bus->number >= port->bridge.conf.secondary_bus &&
+		    bus->number <= port->bridge.conf.subordinate_bus)
 			return port;
 	}
 
@@ -811,7 +646,8 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
 
 	/* Access the emulated PCI-to-PCI bridge */
 	if (bus->number == 0)
-		return mvebu_sw_pci_bridge_write(port, where, size, val);
+		return pci_bridge_emul_conf_write(&port->bridge, where,
+						  size, val);
 
 	if (!mvebu_pcie_link_up(port))
 		return PCIBIOS_DEVICE_NOT_FOUND;
@@ -839,7 +675,8 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
 
 	/* Access the emulated PCI-to-PCI bridge */
 	if (bus->number == 0)
-		return mvebu_sw_pci_bridge_read(port, where, size, val);
+		return pci_bridge_emul_conf_read(&port->bridge, where,
+						 size, val);
 
 	if (!mvebu_pcie_link_up(port)) {
 		*val = 0xffffffff;
@@ -1253,7 +1090,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
 
 		mvebu_pcie_setup_hw(port);
 		mvebu_pcie_set_local_dev_nr(port, 1);
-		mvebu_sw_pci_bridge_init(port);
+		mvebu_pci_bridge_emul_init(port);
 	}
 
 	pcie->nports = i;
-- 
2.14.4

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCHv2 2/3] PCI: mvebu: Convert to PCI emulated bridge config space
@ 2018-09-12 15:48   ` Thomas Petazzoni
  0 siblings, 0 replies; 24+ messages in thread
From: Thomas Petazzoni @ 2018-09-12 15:48 UTC (permalink / raw)
  To: linux-arm-kernel

This commit convers the pci-mvebu driver to use the recently
introduced pci-bridge-emul logic, that helps emulating a root port PCI
bridge configuration space.

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 | 375 +++++++++++--------------------------
 2 files changed, 107 insertions(+), 269 deletions(-)

diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
index 028b287466fb..6d0f9930be7f 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -9,6 +9,7 @@ config PCI_MVEBU
 	depends on MVEBU_MBUS
 	depends on ARM
 	depends on OF
+	select PCI_BRIDGE_EMUL
 
 config PCI_AARDVARK
 	bool "Aardvark PCIe controller"
diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
index 50eb0729385b..f97f48cf2bf6 100644
--- a/drivers/pci/controller/pci-mvebu.c
+++ b/drivers/pci/controller/pci-mvebu.c
@@ -22,6 +22,7 @@
 #include <linux/of_platform.h>
 
 #include "../pci.h"
+#include "../pci-bridge-emul.h"
 
 /*
  * PCIe unit register offsets.
@@ -63,61 +64,6 @@
 #define PCIE_DEBUG_CTRL         0x1a60
 #define  PCIE_DEBUG_SOFT_RESET		BIT(20)
 
-enum {
-	PCISWCAP = PCI_BRIDGE_CONTROL + 2,
-	PCISWCAP_EXP_LIST_ID	= PCISWCAP + PCI_CAP_LIST_ID,
-	PCISWCAP_EXP_DEVCAP	= PCISWCAP + PCI_EXP_DEVCAP,
-	PCISWCAP_EXP_DEVCTL	= PCISWCAP + PCI_EXP_DEVCTL,
-	PCISWCAP_EXP_LNKCAP	= PCISWCAP + PCI_EXP_LNKCAP,
-	PCISWCAP_EXP_LNKCTL	= PCISWCAP + PCI_EXP_LNKCTL,
-	PCISWCAP_EXP_SLTCAP	= PCISWCAP + PCI_EXP_SLTCAP,
-	PCISWCAP_EXP_SLTCTL	= PCISWCAP + PCI_EXP_SLTCTL,
-	PCISWCAP_EXP_RTCTL	= PCISWCAP + PCI_EXP_RTCTL,
-	PCISWCAP_EXP_RTSTA	= PCISWCAP + PCI_EXP_RTSTA,
-	PCISWCAP_EXP_DEVCAP2	= PCISWCAP + PCI_EXP_DEVCAP2,
-	PCISWCAP_EXP_DEVCTL2	= PCISWCAP + PCI_EXP_DEVCTL2,
-	PCISWCAP_EXP_LNKCAP2	= PCISWCAP + PCI_EXP_LNKCAP2,
-	PCISWCAP_EXP_LNKCTL2	= PCISWCAP + PCI_EXP_LNKCTL2,
-	PCISWCAP_EXP_SLTCAP2	= PCISWCAP + PCI_EXP_SLTCAP2,
-	PCISWCAP_EXP_SLTCTL2	= PCISWCAP + PCI_EXP_SLTCTL2,
-};
-
-/* PCI configuration space of a PCI-to-PCI bridge */
-struct mvebu_sw_pci_bridge {
-	u16 vendor;
-	u16 device;
-	u16 command;
-	u16 status;
-	u16 class;
-	u8 interface;
-	u8 revision;
-	u8 bist;
-	u8 header_type;
-	u8 latency_timer;
-	u8 cache_line_size;
-	u32 bar[2];
-	u8 primary_bus;
-	u8 secondary_bus;
-	u8 subordinate_bus;
-	u8 secondary_latency_timer;
-	u8 iobase;
-	u8 iolimit;
-	u16 secondary_status;
-	u16 membase;
-	u16 memlimit;
-	u16 iobaseupper;
-	u16 iolimitupper;
-	u32 romaddr;
-	u8 intline;
-	u8 intpin;
-	u16 bridgectrl;
-
-	/* PCI express capability */
-	u32 pcie_sltcap;
-	u16 pcie_devctl;
-	u16 pcie_rtctl;
-};
-
 struct mvebu_pcie_port;
 
 /* Structure representing all PCIe interfaces */
@@ -153,7 +99,7 @@ struct mvebu_pcie_port {
 	struct clk *clk;
 	struct gpio_desc *reset_gpio;
 	char *reset_name;
-	struct mvebu_sw_pci_bridge bridge;
+	struct pci_bridge_emul bridge;
 	struct device_node *dn;
 	struct mvebu_pcie *pcie;
 	struct mvebu_pcie_window memwin;
@@ -415,11 +361,12 @@ static void mvebu_pcie_set_window(struct mvebu_pcie_port *port,
 static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
 {
 	struct mvebu_pcie_window desired = {};
+	struct pci_bridge_emul_conf *conf = &port->bridge.conf;
 
 	/* Are the new iobase/iolimit values invalid? */
-	if (port->bridge.iolimit < port->bridge.iobase ||
-	    port->bridge.iolimitupper < port->bridge.iobaseupper ||
-	    !(port->bridge.command & PCI_COMMAND_IO)) {
+	if (conf->iolimit < conf->iobase ||
+	    conf->iolimitupper < conf->iobaseupper ||
+	    !(conf->command & PCI_COMMAND_IO)) {
 		mvebu_pcie_set_window(port, port->io_target, port->io_attr,
 				      &desired, &port->iowin);
 		return;
@@ -438,11 +385,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
 	 * specifications. iobase is the bus address, port->iowin_base
 	 * is the CPU address.
 	 */
-	desired.remap = ((port->bridge.iobase & 0xF0) << 8) |
-			(port->bridge.iobaseupper << 16);
+	desired.remap = ((conf->iobase & 0xF0) << 8) |
+			(conf->iobaseupper << 16);
 	desired.base = port->pcie->io.start + desired.remap;
-	desired.size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) |
-			 (port->bridge.iolimitupper << 16)) -
+	desired.size = ((0xFFF | ((conf->iolimit & 0xF0) << 8) |
+			 (conf->iolimitupper << 16)) -
 			desired.remap) +
 		       1;
 
@@ -453,10 +400,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
 static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
 {
 	struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP};
+	struct pci_bridge_emul_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;
@@ -468,130 +416,34 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
 	 * window to setup, according to the PCI-to-PCI bridge
 	 * 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);
 }
 
-/*
- * Initialize the configuration space of the PCI-to-PCI bridge
- * associated with the given PCIe interface.
- */
-static void mvebu_sw_pci_bridge_init(struct mvebu_pcie_port *port)
+static pci_bridge_emul_read_status_t
+mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
+				     int reg, u32 *value)
 {
-	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
-
-	memset(bridge, 0, sizeof(struct mvebu_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;
-}
-
-/*
- * Read the configuration space of the PCI-to-PCI bridge associated to
- * the given PCIe interface.
- */
-static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
-				  unsigned int where, int size, u32 *value)
-{
-	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
-
-	switch (where & ~3) {
-	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;
-
-	case PCI_MEMORY_BASE:
-		*value = (bridge->memlimit << 16 | bridge->membase);
-		break;
-
-	case PCI_PREF_MEMORY_BASE:
-		*value = 0;
-		break;
-
-	case PCI_IO_BASE_UPPER16:
-		*value = (bridge->iolimitupper << 16 | bridge->iobaseupper);
-		break;
-
-	case PCI_CAPABILITY_LIST:
-		*value = PCISWCAP;
-		break;
+	struct mvebu_pcie_port *port = bridge->data;
 
-	case PCI_ROM_ADDRESS1:
-		*value = 0;
-		break;
-
-	case PCI_INTERRUPT_LINE:
-		/* LINE PIN MIN_GNT MAX_LAT */
-		*value = 0;
-		break;
-
-	case PCISWCAP_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;
-
-	case PCISWCAP_EXP_DEVCAP:
+	switch (reg) {
+	case PCI_EXP_DEVCAP:
 		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP);
 		break;
 
-	case PCISWCAP_EXP_DEVCTL:
+	case PCI_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;
 
-	case PCISWCAP_EXP_LNKCAP:
+	case PCI_EXP_LNKCAP:
 		/*
 		 * PCIe requires the clock power management capability to be
 		 * hard-wired to zero for downstream ports
@@ -600,132 +452,88 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
 			 ~PCI_EXP_LNKCAP_CLKPM;
 		break;
 
-	case PCISWCAP_EXP_LNKCTL:
+	case PCI_EXP_LNKCTL:
 		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
 		break;
 
-	case PCISWCAP_EXP_SLTCAP:
-		*value = bridge->pcie_sltcap;
-		break;
-
-	case PCISWCAP_EXP_SLTCTL:
+	case PCI_EXP_SLTCTL:
 		*value = PCI_EXP_SLTSTA_PDS << 16;
 		break;
 
-	case PCISWCAP_EXP_RTCTL:
-		*value = bridge->pcie_rtctl;
-		break;
-
-	case PCISWCAP_EXP_RTSTA:
+	case PCI_EXP_RTSTA:
 		*value = mvebu_readl(port, PCIE_RC_RTSTA);
 		break;
 
-	/* PCIe requires the v2 fields to be hard-wired to zero */
-	case PCISWCAP_EXP_DEVCAP2:
-	case PCISWCAP_EXP_DEVCTL2:
-	case PCISWCAP_EXP_LNKCAP2:
-	case PCISWCAP_EXP_LNKCTL2:
-	case PCISWCAP_EXP_SLTCAP2:
-	case PCISWCAP_EXP_SLTCTL2:
 	default:
-		/*
-		 * PCI defines configuration read accesses to reserved or
-		 * unimplemented registers to read as zero and complete
-		 * normally.
-		 */
-		*value = 0;
-		return PCIBIOS_SUCCESSFUL;
+		return PCI_BRIDGE_EMUL_NOT_HANDLED;
 	}
 
-	if (size == 2)
-		*value = (*value >> (8 * (where & 3))) & 0xffff;
-	else if (size == 1)
-		*value = (*value >> (8 * (where & 3))) & 0xff;
-
-	return PCIBIOS_SUCCESSFUL;
+	return PCI_BRIDGE_EMUL_HANDLED;
 }
 
-/* Write to the PCI-to-PCI bridge configuration space */
-static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
-				     unsigned int where, int size, u32 value)
+static void
+mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge,
+				      int reg, u32 old, u32 new, u32 mask)
 {
-	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
-	u32 mask, reg;
-	int err;
-
-	if (size == 4)
-		mask = 0x0;
-	else if (size == 2)
-		mask = ~(0xffff << ((where & 3) * 8));
-	else if (size == 1)
-		mask = ~(0xff << ((where & 3) * 8));
-	else
-		return PCIBIOS_BAD_REGISTER_NUMBER;
-
-	err = mvebu_sw_pci_bridge_read(port, where & ~3, 4, &reg);
-	if (err)
-		return err;
-
-	value = (reg & mask) | value << ((where & 3) * 8);
+	struct mvebu_pcie_port *port = bridge->data;
+	struct pci_bridge_emul_conf *conf = &bridge->conf;
 
-	switch (where & ~3) {
+	switch (reg) {
 	case PCI_COMMAND:
 	{
-		u32 old = 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;
-	}
 
-	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
+		 * We 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;
+		conf->iobase |= PCI_IO_RANGE_TYPE_32;
+		conf->iolimit |= 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);
+		mvebu_pcie_set_local_bus_nr(port, conf->secondary_bus);
 		break;
 
-	case PCISWCAP_EXP_DEVCTL:
+	default:
+		break;
+	}
+}
+
+static void
+mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+				      int reg, u32 old, u32 new, u32 mask)
+{
+	struct mvebu_pcie_port *port = bridge->data;
+
+	switch (reg) {
+	case PCI_EXP_DEVCTL:
 		/*
 		 * Armada370 data says these bits must always
 		 * be zero when in root complex mode.
 		 */
-		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);
 
 		/*
 		 * If the mask is 0xffff0000, then we only want to write
@@ -733,20 +541,20 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
 		 * RW1C bits in the device status register.  Mask out the
 		 * status register bits.
 		 */
-		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;
 
-	case PCISWCAP_EXP_LNKCTL:
+	case PCI_EXP_LNKCTL:
 		/*
 		 * If we don't support CLKREQ, we must ensure that the
 		 * CLKREQ enable bit always reads zero.  Since we haven't
 		 * had this capability, and it's dependent on board wiring,
 		 * disable it for the time being.
 		 */
-		value &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
+		new &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
 
 		/*
 		 * If the mask is 0xffff0000, then we only want to write
@@ -755,21 +563,48 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
 		 * RW1C status register bits.
 		 */
 		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;
 
-	case PCISWCAP_EXP_RTSTA:
-		mvebu_writel(port, value, PCIE_RC_RTSTA);
+	case PCI_EXP_RTSTA:
+		mvebu_writel(port, new, PCIE_RC_RTSTA);
 		break;
+	}
+}
 
-	default:
-		break;
+struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = {
+	.write_base = mvebu_pci_bridge_emul_base_conf_write,
+	.read_pcie = mvebu_pci_bridge_emul_pcie_conf_read,
+	.write_pcie = mvebu_pci_bridge_emul_pcie_conf_write,
+};
+
+/*
+ * Initialize the configuration space of the PCI-to-PCI bridge
+ * associated with the given PCIe interface.
+ */
+static void mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+{
+	struct pci_bridge_emul *bridge = &port->bridge;
+
+	bridge->conf.vendor = PCI_VENDOR_ID_MARVELL;
+	bridge->conf.device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16;
+	bridge->conf.class_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;
 	}
 
-	return PCIBIOS_SUCCESSFUL;
+	bridge->has_pcie = true;
+	bridge->data = port;
+	bridge->ops = &mvebu_pci_bridge_emul_ops;
+
+	pci_bridge_emul_init(bridge);
 }
 
 static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys)
@@ -789,8 +624,8 @@ static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
 		if (bus->number == 0 && port->devfn == devfn)
 			return port;
 		if (bus->number != 0 &&
-		    bus->number >= port->bridge.secondary_bus &&
-		    bus->number <= port->bridge.subordinate_bus)
+		    bus->number >= port->bridge.conf.secondary_bus &&
+		    bus->number <= port->bridge.conf.subordinate_bus)
 			return port;
 	}
 
@@ -811,7 +646,8 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
 
 	/* Access the emulated PCI-to-PCI bridge */
 	if (bus->number == 0)
-		return mvebu_sw_pci_bridge_write(port, where, size, val);
+		return pci_bridge_emul_conf_write(&port->bridge, where,
+						  size, val);
 
 	if (!mvebu_pcie_link_up(port))
 		return PCIBIOS_DEVICE_NOT_FOUND;
@@ -839,7 +675,8 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
 
 	/* Access the emulated PCI-to-PCI bridge */
 	if (bus->number == 0)
-		return mvebu_sw_pci_bridge_read(port, where, size, val);
+		return pci_bridge_emul_conf_read(&port->bridge, where,
+						 size, val);
 
 	if (!mvebu_pcie_link_up(port)) {
 		*val = 0xffffffff;
@@ -1253,7 +1090,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
 
 		mvebu_pcie_setup_hw(port);
 		mvebu_pcie_set_local_dev_nr(port, 1);
-		mvebu_sw_pci_bridge_init(port);
+		mvebu_pci_bridge_emul_init(port);
 	}
 
 	pcie->nports = i;
-- 
2.14.4

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCHv2 3/3] PCI: aardvark: Implement emulated root PCI bridge config space
  2018-09-12 15:48 ` Thomas Petazzoni
@ 2018-09-12 15:48   ` Thomas Petazzoni
  -1 siblings, 0 replies; 24+ messages in thread
From: Thomas Petazzoni @ 2018-09-12 15:48 UTC (permalink / raw)
  To: Bjorn Helgaas, Lorenzo Pieralisi, Russell King
  Cc: linux-pci, linux-arm-kernel, Gregory Clement, Miquèl Raynal,
	Maxime Chevallier, Antoine Tenart, Nadav Haklai, Zachary Zhang,
	Thomas Petazzoni

From: Zachary Zhang <zhangzg@marvell.com>

The PCI controller in the Marvell Armada 3720 does not implement a
software-accessible root port PCI bridge configuration space. 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 <zhangzg@marvell.com>
[Thomas: convert to the common emulated PCI bridge logic.]
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
 drivers/pci/controller/Kconfig        |   1 +
 drivers/pci/controller/pci-aardvark.c | 129 +++++++++++++++++++++++++++++++++-
 2 files changed, 127 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
index 6d0f9930be7f..5d76ef51532d 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -16,6 +16,7 @@ config PCI_AARDVARK
 	depends on (ARCH_MVEBU && ARM64) || COMPILE_TEST
 	depends on OF
 	depends on PCI_MSI_IRQ_DOMAIN
+	select PCI_BRIDGE_EMUL
 	help
 	 Add support for Aardvark 64bit PCIe Host Controller. This
 	 controller is part of the South Bridge of the Marvel Armada
diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
index 6b4555ff2548..750081c1cb48 100644
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -20,12 +20,16 @@
 #include <linux/of_pci.h>
 
 #include "../pci.h"
+#include "../pci-bridge-emul.h"
 
 /* PCIe core registers */
+#define PCIE_CORE_DEV_ID_REG					0x0
 #define PCIE_CORE_CMD_STATUS_REG				0x4
 #define     PCIE_CORE_CMD_IO_ACCESS_EN				BIT(0)
 #define     PCIE_CORE_CMD_MEM_ACCESS_EN				BIT(1)
 #define     PCIE_CORE_CMD_MEM_IO_REQ_EN				BIT(2)
+#define PCIE_CORE_DEV_REV_REG					0x8
+#define PCIE_CORE_PCIEXP_CAP					0xc0
 #define PCIE_CORE_DEV_CTRL_STATS_REG				0xc8
 #define     PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE	(0 << 4)
 #define     PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT	5
@@ -41,7 +45,10 @@
 #define     PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN			BIT(6)
 #define     PCIE_CORE_ERR_CAPCTL_ECRC_CHCK			BIT(7)
 #define     PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV			BIT(8)
-
+#define     PCIE_CORE_INT_A_ASSERT_ENABLE			1
+#define     PCIE_CORE_INT_B_ASSERT_ENABLE			2
+#define     PCIE_CORE_INT_C_ASSERT_ENABLE			3
+#define     PCIE_CORE_INT_D_ASSERT_ENABLE			4
 /* PIO registers base address and register offsets */
 #define PIO_BASE_ADDR				0x4000
 #define PIO_CTRL				(PIO_BASE_ADDR + 0x0)
@@ -93,7 +100,9 @@
 #define     PCIE_CORE_CTRL2_STRICT_ORDER_ENABLE	BIT(5)
 #define     PCIE_CORE_CTRL2_OB_WIN_ENABLE	BIT(6)
 #define     PCIE_CORE_CTRL2_MSI_ENABLE		BIT(10)
+#define PCIE_MSG_LOG_REG			(CONTROL_BASE_ADDR + 0x30)
 #define PCIE_ISR0_REG				(CONTROL_BASE_ADDR + 0x40)
+#define PCIE_MSG_PM_PME_MASK			BIT(7)
 #define PCIE_ISR0_MASK_REG			(CONTROL_BASE_ADDR + 0x44)
 #define     PCIE_ISR0_MSI_INT_PENDING		BIT(24)
 #define     PCIE_ISR0_INTX_ASSERT(val)		BIT(16 + (val))
@@ -189,6 +198,7 @@ struct advk_pcie {
 	struct mutex msi_used_lock;
 	u16 msi_msg;
 	int root_bus_nr;
+	struct pci_bridge_emul bridge;
 };
 
 static inline void advk_writel(struct advk_pcie *pcie, u32 val, u64 reg)
@@ -390,6 +400,109 @@ static int advk_pcie_wait_pio(struct advk_pcie *pcie)
 	return -ETIMEDOUT;
 }
 
+
+static pci_bridge_emul_read_status_t
+advk_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
+				    int reg, u32 *value)
+{
+	struct advk_pcie *pcie = bridge->data;
+
+
+	switch (reg) {
+	case PCI_EXP_SLTCTL:
+		*value = PCI_EXP_SLTSTA_PDS << 16;
+		return PCI_BRIDGE_EMUL_HANDLED;
+
+	case PCI_EXP_RTCTL: {
+		u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG);
+		*value = (val & PCIE_MSG_PM_PME_MASK) ? PCI_EXP_RTCTL_PMEIE : 0;
+		return PCI_BRIDGE_EMUL_HANDLED;
+	}
+
+	case PCI_EXP_RTSTA: {
+		u32 isr0 = advk_readl(pcie, PCIE_ISR0_REG);
+		u32 msglog = advk_readl(pcie, PCIE_MSG_LOG_REG);
+		*value = (isr0 & PCIE_MSG_PM_PME_MASK) << 16 | (msglog >> 16);
+		return PCI_BRIDGE_EMUL_HANDLED;
+	}
+
+	case PCI_CAP_LIST_ID:
+	case PCI_EXP_DEVCAP:
+	case PCI_EXP_DEVCTL:
+	case PCI_EXP_LNKCAP:
+	case PCI_EXP_LNKCTL:
+		*value = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + reg);
+		return PCI_BRIDGE_EMUL_HANDLED;
+	default:
+		return PCI_BRIDGE_EMUL_NOT_HANDLED;
+	}
+
+}
+
+static void
+advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+				     int reg, u32 old, u32 new, u32 mask)
+{
+	struct advk_pcie *pcie = bridge->data;
+
+	switch (reg) {
+	case PCI_EXP_DEVCTL:
+	case PCI_EXP_LNKCTL:
+		advk_writel(pcie, new, PCIE_CORE_PCIEXP_CAP + reg);
+		break;
+
+	case PCI_EXP_RTCTL:
+		new = (new & PCI_EXP_RTCTL_PMEIE) << 3;
+		advk_writel(pcie, new, PCIE_ISR0_MASK_REG);
+		break;
+
+	case PCI_EXP_RTSTA:
+		new = (new & PCI_EXP_RTSTA_PME) >> 9;
+		advk_writel(pcie, new, PCIE_ISR0_REG);
+		break;
+
+	default:
+		break;
+	}
+}
+
+struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = {
+	.read_pcie = advk_pci_bridge_emul_pcie_conf_read,
+	.write_pcie = advk_pci_bridge_emul_pcie_conf_write,
+};
+
+/*
+ * Initialize the configuration space of the PCI-to-PCI bridge
+ * associated with the given PCIe interface.
+ */
+static void advk_sw_pci_bridge_init(struct advk_pcie *pcie)
+{
+	struct pci_bridge_emul *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.class_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_bridge_emul_ops;
+
+	pci_bridge_emul_init(bridge);
+
+}
+
 static bool advk_pcie_valid_device(struct advk_pcie *pcie, struct pci_bus *bus,
 				  int devfn)
 {
@@ -411,6 +524,10 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn,
 		return PCIBIOS_DEVICE_NOT_FOUND;
 	}
 
+	if (bus->number == pcie->root_bus_nr)
+		return pci_bridge_emul_conf_read(&pcie->bridge, where,
+						 size, val);
+
 	/* Start PIO */
 	advk_writel(pcie, 0, PIO_START);
 	advk_writel(pcie, 1, PIO_ISR);
@@ -418,7 +535,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;
 	else
 		reg |= PCIE_CONFIG_RD_TYPE1;
@@ -463,6 +580,10 @@ static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
 	if (!advk_pcie_valid_device(pcie, bus, devfn))
 		return PCIBIOS_DEVICE_NOT_FOUND;
 
+	if (bus->number == pcie->root_bus_nr)
+		return pci_bridge_emul_conf_write(&pcie->bridge, where,
+						  size, val);
+
 	if (where % size)
 		return PCIBIOS_SET_FAILED;
 
@@ -473,7 +594,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;
 	else
 		reg |= PCIE_CONFIG_WR_TYPE1;
@@ -875,6 +996,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");
-- 
2.14.4

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCHv2 3/3] PCI: aardvark: Implement emulated root PCI bridge config space
@ 2018-09-12 15:48   ` Thomas Petazzoni
  0 siblings, 0 replies; 24+ messages in thread
From: Thomas Petazzoni @ 2018-09-12 15:48 UTC (permalink / raw)
  To: linux-arm-kernel

From: Zachary Zhang <zhangzg@marvell.com>

The PCI controller in the Marvell Armada 3720 does not implement a
software-accessible root port PCI bridge configuration space. 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 <zhangzg@marvell.com>
[Thomas: convert to the common emulated PCI bridge logic.]
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
---
 drivers/pci/controller/Kconfig        |   1 +
 drivers/pci/controller/pci-aardvark.c | 129 +++++++++++++++++++++++++++++++++-
 2 files changed, 127 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
index 6d0f9930be7f..5d76ef51532d 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -16,6 +16,7 @@ config PCI_AARDVARK
 	depends on (ARCH_MVEBU && ARM64) || COMPILE_TEST
 	depends on OF
 	depends on PCI_MSI_IRQ_DOMAIN
+	select PCI_BRIDGE_EMUL
 	help
 	 Add support for Aardvark 64bit PCIe Host Controller. This
 	 controller is part of the South Bridge of the Marvel Armada
diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
index 6b4555ff2548..750081c1cb48 100644
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -20,12 +20,16 @@
 #include <linux/of_pci.h>
 
 #include "../pci.h"
+#include "../pci-bridge-emul.h"
 
 /* PCIe core registers */
+#define PCIE_CORE_DEV_ID_REG					0x0
 #define PCIE_CORE_CMD_STATUS_REG				0x4
 #define     PCIE_CORE_CMD_IO_ACCESS_EN				BIT(0)
 #define     PCIE_CORE_CMD_MEM_ACCESS_EN				BIT(1)
 #define     PCIE_CORE_CMD_MEM_IO_REQ_EN				BIT(2)
+#define PCIE_CORE_DEV_REV_REG					0x8
+#define PCIE_CORE_PCIEXP_CAP					0xc0
 #define PCIE_CORE_DEV_CTRL_STATS_REG				0xc8
 #define     PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE	(0 << 4)
 #define     PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT	5
@@ -41,7 +45,10 @@
 #define     PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN			BIT(6)
 #define     PCIE_CORE_ERR_CAPCTL_ECRC_CHCK			BIT(7)
 #define     PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV			BIT(8)
-
+#define     PCIE_CORE_INT_A_ASSERT_ENABLE			1
+#define     PCIE_CORE_INT_B_ASSERT_ENABLE			2
+#define     PCIE_CORE_INT_C_ASSERT_ENABLE			3
+#define     PCIE_CORE_INT_D_ASSERT_ENABLE			4
 /* PIO registers base address and register offsets */
 #define PIO_BASE_ADDR				0x4000
 #define PIO_CTRL				(PIO_BASE_ADDR + 0x0)
@@ -93,7 +100,9 @@
 #define     PCIE_CORE_CTRL2_STRICT_ORDER_ENABLE	BIT(5)
 #define     PCIE_CORE_CTRL2_OB_WIN_ENABLE	BIT(6)
 #define     PCIE_CORE_CTRL2_MSI_ENABLE		BIT(10)
+#define PCIE_MSG_LOG_REG			(CONTROL_BASE_ADDR + 0x30)
 #define PCIE_ISR0_REG				(CONTROL_BASE_ADDR + 0x40)
+#define PCIE_MSG_PM_PME_MASK			BIT(7)
 #define PCIE_ISR0_MASK_REG			(CONTROL_BASE_ADDR + 0x44)
 #define     PCIE_ISR0_MSI_INT_PENDING		BIT(24)
 #define     PCIE_ISR0_INTX_ASSERT(val)		BIT(16 + (val))
@@ -189,6 +198,7 @@ struct advk_pcie {
 	struct mutex msi_used_lock;
 	u16 msi_msg;
 	int root_bus_nr;
+	struct pci_bridge_emul bridge;
 };
 
 static inline void advk_writel(struct advk_pcie *pcie, u32 val, u64 reg)
@@ -390,6 +400,109 @@ static int advk_pcie_wait_pio(struct advk_pcie *pcie)
 	return -ETIMEDOUT;
 }
 
+
+static pci_bridge_emul_read_status_t
+advk_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
+				    int reg, u32 *value)
+{
+	struct advk_pcie *pcie = bridge->data;
+
+
+	switch (reg) {
+	case PCI_EXP_SLTCTL:
+		*value = PCI_EXP_SLTSTA_PDS << 16;
+		return PCI_BRIDGE_EMUL_HANDLED;
+
+	case PCI_EXP_RTCTL: {
+		u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG);
+		*value = (val & PCIE_MSG_PM_PME_MASK) ? PCI_EXP_RTCTL_PMEIE : 0;
+		return PCI_BRIDGE_EMUL_HANDLED;
+	}
+
+	case PCI_EXP_RTSTA: {
+		u32 isr0 = advk_readl(pcie, PCIE_ISR0_REG);
+		u32 msglog = advk_readl(pcie, PCIE_MSG_LOG_REG);
+		*value = (isr0 & PCIE_MSG_PM_PME_MASK) << 16 | (msglog >> 16);
+		return PCI_BRIDGE_EMUL_HANDLED;
+	}
+
+	case PCI_CAP_LIST_ID:
+	case PCI_EXP_DEVCAP:
+	case PCI_EXP_DEVCTL:
+	case PCI_EXP_LNKCAP:
+	case PCI_EXP_LNKCTL:
+		*value = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + reg);
+		return PCI_BRIDGE_EMUL_HANDLED;
+	default:
+		return PCI_BRIDGE_EMUL_NOT_HANDLED;
+	}
+
+}
+
+static void
+advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+				     int reg, u32 old, u32 new, u32 mask)
+{
+	struct advk_pcie *pcie = bridge->data;
+
+	switch (reg) {
+	case PCI_EXP_DEVCTL:
+	case PCI_EXP_LNKCTL:
+		advk_writel(pcie, new, PCIE_CORE_PCIEXP_CAP + reg);
+		break;
+
+	case PCI_EXP_RTCTL:
+		new = (new & PCI_EXP_RTCTL_PMEIE) << 3;
+		advk_writel(pcie, new, PCIE_ISR0_MASK_REG);
+		break;
+
+	case PCI_EXP_RTSTA:
+		new = (new & PCI_EXP_RTSTA_PME) >> 9;
+		advk_writel(pcie, new, PCIE_ISR0_REG);
+		break;
+
+	default:
+		break;
+	}
+}
+
+struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = {
+	.read_pcie = advk_pci_bridge_emul_pcie_conf_read,
+	.write_pcie = advk_pci_bridge_emul_pcie_conf_write,
+};
+
+/*
+ * Initialize the configuration space of the PCI-to-PCI bridge
+ * associated with the given PCIe interface.
+ */
+static void advk_sw_pci_bridge_init(struct advk_pcie *pcie)
+{
+	struct pci_bridge_emul *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.class_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_bridge_emul_ops;
+
+	pci_bridge_emul_init(bridge);
+
+}
+
 static bool advk_pcie_valid_device(struct advk_pcie *pcie, struct pci_bus *bus,
 				  int devfn)
 {
@@ -411,6 +524,10 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn,
 		return PCIBIOS_DEVICE_NOT_FOUND;
 	}
 
+	if (bus->number == pcie->root_bus_nr)
+		return pci_bridge_emul_conf_read(&pcie->bridge, where,
+						 size, val);
+
 	/* Start PIO */
 	advk_writel(pcie, 0, PIO_START);
 	advk_writel(pcie, 1, PIO_ISR);
@@ -418,7 +535,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;
 	else
 		reg |= PCIE_CONFIG_RD_TYPE1;
@@ -463,6 +580,10 @@ static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
 	if (!advk_pcie_valid_device(pcie, bus, devfn))
 		return PCIBIOS_DEVICE_NOT_FOUND;
 
+	if (bus->number == pcie->root_bus_nr)
+		return pci_bridge_emul_conf_write(&pcie->bridge, where,
+						  size, val);
+
 	if (where % size)
 		return PCIBIOS_SET_FAILED;
 
@@ -473,7 +594,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;
 	else
 		reg |= PCIE_CONFIG_WR_TYPE1;
@@ -875,6 +996,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");
-- 
2.14.4

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 1/3] PCI: Introduce PCI bridge emulated config space common logic
  2018-09-12 15:48   ` Thomas Petazzoni
@ 2018-09-12 18:53     ` Bjorn Helgaas
  -1 siblings, 0 replies; 24+ messages in thread
From: Bjorn Helgaas @ 2018-09-12 18:53 UTC (permalink / raw)
  To: Thomas Petazzoni
  Cc: Lorenzo Pieralisi, Russell King, Antoine Tenart, linux-pci,
	Gregory Clement, Maxime Chevallier, Nadav Haklai,
	Miquèl Raynal, Bjorn Helgaas, linux-arm-kernel

On Wed, Sep 12, 2018 at 05:48:29PM +0200, Thomas Petazzoni wrote:
> 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>

Acked-by: Bjorn Helgaas <bhelgaas@google.com>

Looks nice!  Lorenzo, if/when you're happy with these, please merge them
all through your tree.

> +#define PCI_BRIDGE_CONF_END	(PCI_BRIDGE_CONTROL + 2)

I think PCI_STD_HEADER_SIZEOF might be a better base than
PCI_BRIDGE_CONTROL, since there's no connection between
the location of PCI_BRIDGE_CONTROL and anything else.

> +#define PCI_CAP_PCIE_START	PCI_BRIDGE_CONF_END
> +#define PCI_CAP_PCIE_END	(PCI_CAP_PCIE_START + PCI_EXP_SLTSTA2 + 2)

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCHv2 1/3] PCI: Introduce PCI bridge emulated config space common logic
@ 2018-09-12 18:53     ` Bjorn Helgaas
  0 siblings, 0 replies; 24+ messages in thread
From: Bjorn Helgaas @ 2018-09-12 18:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Sep 12, 2018 at 05:48:29PM +0200, Thomas Petazzoni wrote:
> 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>

Acked-by: Bjorn Helgaas <bhelgaas@google.com>

Looks nice!  Lorenzo, if/when you're happy with these, please merge them
all through your tree.

> +#define PCI_BRIDGE_CONF_END	(PCI_BRIDGE_CONTROL + 2)

I think PCI_STD_HEADER_SIZEOF might be a better base than
PCI_BRIDGE_CONTROL, since there's no connection between
the location of PCI_BRIDGE_CONTROL and anything else.

> +#define PCI_CAP_PCIE_START	PCI_BRIDGE_CONF_END
> +#define PCI_CAP_PCIE_END	(PCI_CAP_PCIE_START + PCI_EXP_SLTSTA2 + 2)

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 1/3] PCI: Introduce PCI bridge emulated config space common logic
@ 2018-09-14 14:38     ` Russell King - ARM Linux
  0 siblings, 0 replies; 24+ messages in thread
From: Russell King - ARM Linux @ 2018-09-14 14:38 UTC (permalink / raw)
  To: Thomas Petazzoni
  Cc: Lorenzo Pieralisi, Antoine Tenart, linux-pci, Gregory Clement,
	Maxime Chevallier, Nadav Haklai, Miquèl Raynal,
	Bjorn Helgaas, linux-arm-kernel

On Wed, Sep 12, 2018 at 05:48:29PM +0200, Thomas Petazzoni wrote:
> 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.

Thanks, for the patch.  I don't see anything technically wrong from
a PCIe device behaviour point of view, although I haven't compared
the behaviour tables with the specs.

Reviewed-by: Russell King <rmk+kernel@armlinux.org.uk>

-- 
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

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 1/3] PCI: Introduce PCI bridge emulated config space common logic
@ 2018-09-14 14:38     ` Russell King - ARM Linux
  0 siblings, 0 replies; 24+ messages in thread
From: Russell King - ARM Linux @ 2018-09-14 14:38 UTC (permalink / raw)
  To: Thomas Petazzoni
  Cc: Bjorn Helgaas, Lorenzo Pieralisi, linux-pci, linux-arm-kernel,
	Gregory Clement, Miquèl Raynal, Maxime Chevallier,
	Antoine Tenart, Nadav Haklai

On Wed, Sep 12, 2018 at 05:48:29PM +0200, Thomas Petazzoni wrote:
> 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.

Thanks, for the patch.  I don't see anything technically wrong from
a PCIe device behaviour point of view, although I haven't compared
the behaviour tables with the specs.

Reviewed-by: Russell King <rmk+kernel@armlinux.org.uk>

-- 
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

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCHv2 1/3] PCI: Introduce PCI bridge emulated config space common logic
@ 2018-09-14 14:38     ` Russell King - ARM Linux
  0 siblings, 0 replies; 24+ messages in thread
From: Russell King - ARM Linux @ 2018-09-14 14:38 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Sep 12, 2018 at 05:48:29PM +0200, Thomas Petazzoni wrote:
> 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.

Thanks, for the patch.  I don't see anything technically wrong from
a PCIe device behaviour point of view, although I haven't compared
the behaviour tables with the specs.

Reviewed-by: Russell King <rmk+kernel@armlinux.org.uk>

-- 
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

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 2/3] PCI: mvebu: Convert to PCI emulated bridge config space
  2018-09-12 15:48   ` Thomas Petazzoni
@ 2018-09-14 14:44     ` Russell King - ARM Linux
  -1 siblings, 0 replies; 24+ messages in thread
From: Russell King - ARM Linux @ 2018-09-14 14:44 UTC (permalink / raw)
  To: Thomas Petazzoni
  Cc: Bjorn Helgaas, Lorenzo Pieralisi, linux-pci, linux-arm-kernel,
	Gregory Clement, Miquèl Raynal, Maxime Chevallier,
	Antoine Tenart, Nadav Haklai

On Wed, Sep 12, 2018 at 05:48:30PM +0200, Thomas Petazzoni wrote:
> This commit convers the pci-mvebu driver to use the recently
> introduced pci-bridge-emul logic, that helps emulating a root port PCI
> bridge configuration space.
> 
> It has been tested on Armada GP XP, with a E1000E NIC.

I think this needs commentry explaining the "FIXME":

> -	case PCISWCAP_EXP_DEVCTL:
> +	case PCI_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;

otherwise it's likely to end up remaining there.

-- 
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

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCHv2 2/3] PCI: mvebu: Convert to PCI emulated bridge config space
@ 2018-09-14 14:44     ` Russell King - ARM Linux
  0 siblings, 0 replies; 24+ messages in thread
From: Russell King - ARM Linux @ 2018-09-14 14:44 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Sep 12, 2018 at 05:48:30PM +0200, Thomas Petazzoni wrote:
> This commit convers the pci-mvebu driver to use the recently
> introduced pci-bridge-emul logic, that helps emulating a root port PCI
> bridge configuration space.
> 
> It has been tested on Armada GP XP, with a E1000E NIC.

I think this needs commentry explaining the "FIXME":

> -	case PCISWCAP_EXP_DEVCTL:
> +	case PCI_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;

otherwise it's likely to end up remaining there.

-- 
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

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 2/3] PCI: mvebu: Convert to PCI emulated bridge config space
@ 2018-09-18 16:17     ` Lorenzo Pieralisi
  0 siblings, 0 replies; 24+ messages in thread
From: Lorenzo Pieralisi @ 2018-09-18 16:17 UTC (permalink / raw)
  To: Thomas Petazzoni
  Cc: Russell King, Antoine Tenart, linux-pci, Gregory Clement,
	Maxime Chevallier, Nadav Haklai, Miqu??l Raynal, Bjorn Helgaas,
	linux-arm-kernel

On Wed, Sep 12, 2018 at 05:48:30PM +0200, Thomas Petazzoni wrote:
> This commit convers the pci-mvebu driver to use the recently

Nits:

s/convers/converts

Actually I would just write "Convert the pci-mvebu....".

I would drop "recently introduced", unless we can define a precise
commit when code was added so that it can actually be checked (I know
you can't since the relevant patch is part of this series and not in
the mainline yet).

Bjorn posted some guidelines that are helpful:

https://marc.info/?l=linux-pci&m=150905742808166&w=2

I can make these changes myself, no problem but please address Russell's
comment so that we can proceed.

Thanks,
Lorenzo

> introduced pci-bridge-emul logic, that helps emulating a root port PCI
> bridge configuration space.
> 
> 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 | 375 +++++++++++--------------------------
>  2 files changed, 107 insertions(+), 269 deletions(-)
> 
> diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
> index 028b287466fb..6d0f9930be7f 100644
> --- a/drivers/pci/controller/Kconfig
> +++ b/drivers/pci/controller/Kconfig
> @@ -9,6 +9,7 @@ config PCI_MVEBU
>  	depends on MVEBU_MBUS
>  	depends on ARM
>  	depends on OF
> +	select PCI_BRIDGE_EMUL
>  
>  config PCI_AARDVARK
>  	bool "Aardvark PCIe controller"
> diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
> index 50eb0729385b..f97f48cf2bf6 100644
> --- a/drivers/pci/controller/pci-mvebu.c
> +++ b/drivers/pci/controller/pci-mvebu.c
> @@ -22,6 +22,7 @@
>  #include <linux/of_platform.h>
>  
>  #include "../pci.h"
> +#include "../pci-bridge-emul.h"
>  
>  /*
>   * PCIe unit register offsets.
> @@ -63,61 +64,6 @@
>  #define PCIE_DEBUG_CTRL         0x1a60
>  #define  PCIE_DEBUG_SOFT_RESET		BIT(20)
>  
> -enum {
> -	PCISWCAP = PCI_BRIDGE_CONTROL + 2,
> -	PCISWCAP_EXP_LIST_ID	= PCISWCAP + PCI_CAP_LIST_ID,
> -	PCISWCAP_EXP_DEVCAP	= PCISWCAP + PCI_EXP_DEVCAP,
> -	PCISWCAP_EXP_DEVCTL	= PCISWCAP + PCI_EXP_DEVCTL,
> -	PCISWCAP_EXP_LNKCAP	= PCISWCAP + PCI_EXP_LNKCAP,
> -	PCISWCAP_EXP_LNKCTL	= PCISWCAP + PCI_EXP_LNKCTL,
> -	PCISWCAP_EXP_SLTCAP	= PCISWCAP + PCI_EXP_SLTCAP,
> -	PCISWCAP_EXP_SLTCTL	= PCISWCAP + PCI_EXP_SLTCTL,
> -	PCISWCAP_EXP_RTCTL	= PCISWCAP + PCI_EXP_RTCTL,
> -	PCISWCAP_EXP_RTSTA	= PCISWCAP + PCI_EXP_RTSTA,
> -	PCISWCAP_EXP_DEVCAP2	= PCISWCAP + PCI_EXP_DEVCAP2,
> -	PCISWCAP_EXP_DEVCTL2	= PCISWCAP + PCI_EXP_DEVCTL2,
> -	PCISWCAP_EXP_LNKCAP2	= PCISWCAP + PCI_EXP_LNKCAP2,
> -	PCISWCAP_EXP_LNKCTL2	= PCISWCAP + PCI_EXP_LNKCTL2,
> -	PCISWCAP_EXP_SLTCAP2	= PCISWCAP + PCI_EXP_SLTCAP2,
> -	PCISWCAP_EXP_SLTCTL2	= PCISWCAP + PCI_EXP_SLTCTL2,
> -};
> -
> -/* PCI configuration space of a PCI-to-PCI bridge */
> -struct mvebu_sw_pci_bridge {
> -	u16 vendor;
> -	u16 device;
> -	u16 command;
> -	u16 status;
> -	u16 class;
> -	u8 interface;
> -	u8 revision;
> -	u8 bist;
> -	u8 header_type;
> -	u8 latency_timer;
> -	u8 cache_line_size;
> -	u32 bar[2];
> -	u8 primary_bus;
> -	u8 secondary_bus;
> -	u8 subordinate_bus;
> -	u8 secondary_latency_timer;
> -	u8 iobase;
> -	u8 iolimit;
> -	u16 secondary_status;
> -	u16 membase;
> -	u16 memlimit;
> -	u16 iobaseupper;
> -	u16 iolimitupper;
> -	u32 romaddr;
> -	u8 intline;
> -	u8 intpin;
> -	u16 bridgectrl;
> -
> -	/* PCI express capability */
> -	u32 pcie_sltcap;
> -	u16 pcie_devctl;
> -	u16 pcie_rtctl;
> -};
> -
>  struct mvebu_pcie_port;
>  
>  /* Structure representing all PCIe interfaces */
> @@ -153,7 +99,7 @@ struct mvebu_pcie_port {
>  	struct clk *clk;
>  	struct gpio_desc *reset_gpio;
>  	char *reset_name;
> -	struct mvebu_sw_pci_bridge bridge;
> +	struct pci_bridge_emul bridge;
>  	struct device_node *dn;
>  	struct mvebu_pcie *pcie;
>  	struct mvebu_pcie_window memwin;
> @@ -415,11 +361,12 @@ static void mvebu_pcie_set_window(struct mvebu_pcie_port *port,
>  static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
>  {
>  	struct mvebu_pcie_window desired = {};
> +	struct pci_bridge_emul_conf *conf = &port->bridge.conf;
>  
>  	/* Are the new iobase/iolimit values invalid? */
> -	if (port->bridge.iolimit < port->bridge.iobase ||
> -	    port->bridge.iolimitupper < port->bridge.iobaseupper ||
> -	    !(port->bridge.command & PCI_COMMAND_IO)) {
> +	if (conf->iolimit < conf->iobase ||
> +	    conf->iolimitupper < conf->iobaseupper ||
> +	    !(conf->command & PCI_COMMAND_IO)) {
>  		mvebu_pcie_set_window(port, port->io_target, port->io_attr,
>  				      &desired, &port->iowin);
>  		return;
> @@ -438,11 +385,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
>  	 * specifications. iobase is the bus address, port->iowin_base
>  	 * is the CPU address.
>  	 */
> -	desired.remap = ((port->bridge.iobase & 0xF0) << 8) |
> -			(port->bridge.iobaseupper << 16);
> +	desired.remap = ((conf->iobase & 0xF0) << 8) |
> +			(conf->iobaseupper << 16);
>  	desired.base = port->pcie->io.start + desired.remap;
> -	desired.size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) |
> -			 (port->bridge.iolimitupper << 16)) -
> +	desired.size = ((0xFFF | ((conf->iolimit & 0xF0) << 8) |
> +			 (conf->iolimitupper << 16)) -
>  			desired.remap) +
>  		       1;
>  
> @@ -453,10 +400,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
>  static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
>  {
>  	struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP};
> +	struct pci_bridge_emul_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;
> @@ -468,130 +416,34 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
>  	 * window to setup, according to the PCI-to-PCI bridge
>  	 * 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);
>  }
>  
> -/*
> - * Initialize the configuration space of the PCI-to-PCI bridge
> - * associated with the given PCIe interface.
> - */
> -static void mvebu_sw_pci_bridge_init(struct mvebu_pcie_port *port)
> +static pci_bridge_emul_read_status_t
> +mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
> +				     int reg, u32 *value)
>  {
> -	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
> -
> -	memset(bridge, 0, sizeof(struct mvebu_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;
> -}
> -
> -/*
> - * Read the configuration space of the PCI-to-PCI bridge associated to
> - * the given PCIe interface.
> - */
> -static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
> -				  unsigned int where, int size, u32 *value)
> -{
> -	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
> -
> -	switch (where & ~3) {
> -	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;
> -
> -	case PCI_MEMORY_BASE:
> -		*value = (bridge->memlimit << 16 | bridge->membase);
> -		break;
> -
> -	case PCI_PREF_MEMORY_BASE:
> -		*value = 0;
> -		break;
> -
> -	case PCI_IO_BASE_UPPER16:
> -		*value = (bridge->iolimitupper << 16 | bridge->iobaseupper);
> -		break;
> -
> -	case PCI_CAPABILITY_LIST:
> -		*value = PCISWCAP;
> -		break;
> +	struct mvebu_pcie_port *port = bridge->data;
>  
> -	case PCI_ROM_ADDRESS1:
> -		*value = 0;
> -		break;
> -
> -	case PCI_INTERRUPT_LINE:
> -		/* LINE PIN MIN_GNT MAX_LAT */
> -		*value = 0;
> -		break;
> -
> -	case PCISWCAP_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;
> -
> -	case PCISWCAP_EXP_DEVCAP:
> +	switch (reg) {
> +	case PCI_EXP_DEVCAP:
>  		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP);
>  		break;
>  
> -	case PCISWCAP_EXP_DEVCTL:
> +	case PCI_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;
>  
> -	case PCISWCAP_EXP_LNKCAP:
> +	case PCI_EXP_LNKCAP:
>  		/*
>  		 * PCIe requires the clock power management capability to be
>  		 * hard-wired to zero for downstream ports
> @@ -600,132 +452,88 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
>  			 ~PCI_EXP_LNKCAP_CLKPM;
>  		break;
>  
> -	case PCISWCAP_EXP_LNKCTL:
> +	case PCI_EXP_LNKCTL:
>  		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
>  		break;
>  
> -	case PCISWCAP_EXP_SLTCAP:
> -		*value = bridge->pcie_sltcap;
> -		break;
> -
> -	case PCISWCAP_EXP_SLTCTL:
> +	case PCI_EXP_SLTCTL:
>  		*value = PCI_EXP_SLTSTA_PDS << 16;
>  		break;
>  
> -	case PCISWCAP_EXP_RTCTL:
> -		*value = bridge->pcie_rtctl;
> -		break;
> -
> -	case PCISWCAP_EXP_RTSTA:
> +	case PCI_EXP_RTSTA:
>  		*value = mvebu_readl(port, PCIE_RC_RTSTA);
>  		break;
>  
> -	/* PCIe requires the v2 fields to be hard-wired to zero */
> -	case PCISWCAP_EXP_DEVCAP2:
> -	case PCISWCAP_EXP_DEVCTL2:
> -	case PCISWCAP_EXP_LNKCAP2:
> -	case PCISWCAP_EXP_LNKCTL2:
> -	case PCISWCAP_EXP_SLTCAP2:
> -	case PCISWCAP_EXP_SLTCTL2:
>  	default:
> -		/*
> -		 * PCI defines configuration read accesses to reserved or
> -		 * unimplemented registers to read as zero and complete
> -		 * normally.
> -		 */
> -		*value = 0;
> -		return PCIBIOS_SUCCESSFUL;
> +		return PCI_BRIDGE_EMUL_NOT_HANDLED;
>  	}
>  
> -	if (size == 2)
> -		*value = (*value >> (8 * (where & 3))) & 0xffff;
> -	else if (size == 1)
> -		*value = (*value >> (8 * (where & 3))) & 0xff;
> -
> -	return PCIBIOS_SUCCESSFUL;
> +	return PCI_BRIDGE_EMUL_HANDLED;
>  }
>  
> -/* Write to the PCI-to-PCI bridge configuration space */
> -static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
> -				     unsigned int where, int size, u32 value)
> +static void
> +mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge,
> +				      int reg, u32 old, u32 new, u32 mask)
>  {
> -	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
> -	u32 mask, reg;
> -	int err;
> -
> -	if (size == 4)
> -		mask = 0x0;
> -	else if (size == 2)
> -		mask = ~(0xffff << ((where & 3) * 8));
> -	else if (size == 1)
> -		mask = ~(0xff << ((where & 3) * 8));
> -	else
> -		return PCIBIOS_BAD_REGISTER_NUMBER;
> -
> -	err = mvebu_sw_pci_bridge_read(port, where & ~3, 4, &reg);
> -	if (err)
> -		return err;
> -
> -	value = (reg & mask) | value << ((where & 3) * 8);
> +	struct mvebu_pcie_port *port = bridge->data;
> +	struct pci_bridge_emul_conf *conf = &bridge->conf;
>  
> -	switch (where & ~3) {
> +	switch (reg) {
>  	case PCI_COMMAND:
>  	{
> -		u32 old = 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;
> -	}
>  
> -	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
> +		 * We 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;
> +		conf->iobase |= PCI_IO_RANGE_TYPE_32;
> +		conf->iolimit |= 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);
> +		mvebu_pcie_set_local_bus_nr(port, conf->secondary_bus);
>  		break;
>  
> -	case PCISWCAP_EXP_DEVCTL:
> +	default:
> +		break;
> +	}
> +}
> +
> +static void
> +mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
> +				      int reg, u32 old, u32 new, u32 mask)
> +{
> +	struct mvebu_pcie_port *port = bridge->data;
> +
> +	switch (reg) {
> +	case PCI_EXP_DEVCTL:
>  		/*
>  		 * Armada370 data says these bits must always
>  		 * be zero when in root complex mode.
>  		 */
> -		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);
>  
>  		/*
>  		 * If the mask is 0xffff0000, then we only want to write
> @@ -733,20 +541,20 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
>  		 * RW1C bits in the device status register.  Mask out the
>  		 * status register bits.
>  		 */
> -		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;
>  
> -	case PCISWCAP_EXP_LNKCTL:
> +	case PCI_EXP_LNKCTL:
>  		/*
>  		 * If we don't support CLKREQ, we must ensure that the
>  		 * CLKREQ enable bit always reads zero.  Since we haven't
>  		 * had this capability, and it's dependent on board wiring,
>  		 * disable it for the time being.
>  		 */
> -		value &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
> +		new &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
>  
>  		/*
>  		 * If the mask is 0xffff0000, then we only want to write
> @@ -755,21 +563,48 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
>  		 * RW1C status register bits.
>  		 */
>  		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;
>  
> -	case PCISWCAP_EXP_RTSTA:
> -		mvebu_writel(port, value, PCIE_RC_RTSTA);
> +	case PCI_EXP_RTSTA:
> +		mvebu_writel(port, new, PCIE_RC_RTSTA);
>  		break;
> +	}
> +}
>  
> -	default:
> -		break;
> +struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = {
> +	.write_base = mvebu_pci_bridge_emul_base_conf_write,
> +	.read_pcie = mvebu_pci_bridge_emul_pcie_conf_read,
> +	.write_pcie = mvebu_pci_bridge_emul_pcie_conf_write,
> +};
> +
> +/*
> + * Initialize the configuration space of the PCI-to-PCI bridge
> + * associated with the given PCIe interface.
> + */
> +static void mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
> +{
> +	struct pci_bridge_emul *bridge = &port->bridge;
> +
> +	bridge->conf.vendor = PCI_VENDOR_ID_MARVELL;
> +	bridge->conf.device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16;
> +	bridge->conf.class_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;
>  	}
>  
> -	return PCIBIOS_SUCCESSFUL;
> +	bridge->has_pcie = true;
> +	bridge->data = port;
> +	bridge->ops = &mvebu_pci_bridge_emul_ops;
> +
> +	pci_bridge_emul_init(bridge);
>  }
>  
>  static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys)
> @@ -789,8 +624,8 @@ static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
>  		if (bus->number == 0 && port->devfn == devfn)
>  			return port;
>  		if (bus->number != 0 &&
> -		    bus->number >= port->bridge.secondary_bus &&
> -		    bus->number <= port->bridge.subordinate_bus)
> +		    bus->number >= port->bridge.conf.secondary_bus &&
> +		    bus->number <= port->bridge.conf.subordinate_bus)
>  			return port;
>  	}
>  
> @@ -811,7 +646,8 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
>  
>  	/* Access the emulated PCI-to-PCI bridge */
>  	if (bus->number == 0)
> -		return mvebu_sw_pci_bridge_write(port, where, size, val);
> +		return pci_bridge_emul_conf_write(&port->bridge, where,
> +						  size, val);
>  
>  	if (!mvebu_pcie_link_up(port))
>  		return PCIBIOS_DEVICE_NOT_FOUND;
> @@ -839,7 +675,8 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
>  
>  	/* Access the emulated PCI-to-PCI bridge */
>  	if (bus->number == 0)
> -		return mvebu_sw_pci_bridge_read(port, where, size, val);
> +		return pci_bridge_emul_conf_read(&port->bridge, where,
> +						 size, val);
>  
>  	if (!mvebu_pcie_link_up(port)) {
>  		*val = 0xffffffff;
> @@ -1253,7 +1090,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
>  
>  		mvebu_pcie_setup_hw(port);
>  		mvebu_pcie_set_local_dev_nr(port, 1);
> -		mvebu_sw_pci_bridge_init(port);
> +		mvebu_pci_bridge_emul_init(port);
>  	}
>  
>  	pcie->nports = i;
> -- 
> 2.14.4
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 2/3] PCI: mvebu: Convert to PCI emulated bridge config space
@ 2018-09-18 16:17     ` Lorenzo Pieralisi
  0 siblings, 0 replies; 24+ messages in thread
From: Lorenzo Pieralisi @ 2018-09-18 16:17 UTC (permalink / raw)
  To: Thomas Petazzoni
  Cc: Bjorn Helgaas, Russell King, linux-pci, linux-arm-kernel,
	Gregory Clement, Miqu??l Raynal, Maxime Chevallier,
	Antoine Tenart, Nadav Haklai

On Wed, Sep 12, 2018 at 05:48:30PM +0200, Thomas Petazzoni wrote:
> This commit convers the pci-mvebu driver to use the recently

Nits:

s/convers/converts

Actually I would just write "Convert the pci-mvebu....".

I would drop "recently introduced", unless we can define a precise
commit when code was added so that it can actually be checked (I know
you can't since the relevant patch is part of this series and not in
the mainline yet).

Bjorn posted some guidelines that are helpful:

https://marc.info/?l=linux-pci&m=150905742808166&w=2

I can make these changes myself, no problem but please address Russell's
comment so that we can proceed.

Thanks,
Lorenzo

> introduced pci-bridge-emul logic, that helps emulating a root port PCI
> bridge configuration space.
> 
> 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 | 375 +++++++++++--------------------------
>  2 files changed, 107 insertions(+), 269 deletions(-)
> 
> diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
> index 028b287466fb..6d0f9930be7f 100644
> --- a/drivers/pci/controller/Kconfig
> +++ b/drivers/pci/controller/Kconfig
> @@ -9,6 +9,7 @@ config PCI_MVEBU
>  	depends on MVEBU_MBUS
>  	depends on ARM
>  	depends on OF
> +	select PCI_BRIDGE_EMUL
>  
>  config PCI_AARDVARK
>  	bool "Aardvark PCIe controller"
> diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
> index 50eb0729385b..f97f48cf2bf6 100644
> --- a/drivers/pci/controller/pci-mvebu.c
> +++ b/drivers/pci/controller/pci-mvebu.c
> @@ -22,6 +22,7 @@
>  #include <linux/of_platform.h>
>  
>  #include "../pci.h"
> +#include "../pci-bridge-emul.h"
>  
>  /*
>   * PCIe unit register offsets.
> @@ -63,61 +64,6 @@
>  #define PCIE_DEBUG_CTRL         0x1a60
>  #define  PCIE_DEBUG_SOFT_RESET		BIT(20)
>  
> -enum {
> -	PCISWCAP = PCI_BRIDGE_CONTROL + 2,
> -	PCISWCAP_EXP_LIST_ID	= PCISWCAP + PCI_CAP_LIST_ID,
> -	PCISWCAP_EXP_DEVCAP	= PCISWCAP + PCI_EXP_DEVCAP,
> -	PCISWCAP_EXP_DEVCTL	= PCISWCAP + PCI_EXP_DEVCTL,
> -	PCISWCAP_EXP_LNKCAP	= PCISWCAP + PCI_EXP_LNKCAP,
> -	PCISWCAP_EXP_LNKCTL	= PCISWCAP + PCI_EXP_LNKCTL,
> -	PCISWCAP_EXP_SLTCAP	= PCISWCAP + PCI_EXP_SLTCAP,
> -	PCISWCAP_EXP_SLTCTL	= PCISWCAP + PCI_EXP_SLTCTL,
> -	PCISWCAP_EXP_RTCTL	= PCISWCAP + PCI_EXP_RTCTL,
> -	PCISWCAP_EXP_RTSTA	= PCISWCAP + PCI_EXP_RTSTA,
> -	PCISWCAP_EXP_DEVCAP2	= PCISWCAP + PCI_EXP_DEVCAP2,
> -	PCISWCAP_EXP_DEVCTL2	= PCISWCAP + PCI_EXP_DEVCTL2,
> -	PCISWCAP_EXP_LNKCAP2	= PCISWCAP + PCI_EXP_LNKCAP2,
> -	PCISWCAP_EXP_LNKCTL2	= PCISWCAP + PCI_EXP_LNKCTL2,
> -	PCISWCAP_EXP_SLTCAP2	= PCISWCAP + PCI_EXP_SLTCAP2,
> -	PCISWCAP_EXP_SLTCTL2	= PCISWCAP + PCI_EXP_SLTCTL2,
> -};
> -
> -/* PCI configuration space of a PCI-to-PCI bridge */
> -struct mvebu_sw_pci_bridge {
> -	u16 vendor;
> -	u16 device;
> -	u16 command;
> -	u16 status;
> -	u16 class;
> -	u8 interface;
> -	u8 revision;
> -	u8 bist;
> -	u8 header_type;
> -	u8 latency_timer;
> -	u8 cache_line_size;
> -	u32 bar[2];
> -	u8 primary_bus;
> -	u8 secondary_bus;
> -	u8 subordinate_bus;
> -	u8 secondary_latency_timer;
> -	u8 iobase;
> -	u8 iolimit;
> -	u16 secondary_status;
> -	u16 membase;
> -	u16 memlimit;
> -	u16 iobaseupper;
> -	u16 iolimitupper;
> -	u32 romaddr;
> -	u8 intline;
> -	u8 intpin;
> -	u16 bridgectrl;
> -
> -	/* PCI express capability */
> -	u32 pcie_sltcap;
> -	u16 pcie_devctl;
> -	u16 pcie_rtctl;
> -};
> -
>  struct mvebu_pcie_port;
>  
>  /* Structure representing all PCIe interfaces */
> @@ -153,7 +99,7 @@ struct mvebu_pcie_port {
>  	struct clk *clk;
>  	struct gpio_desc *reset_gpio;
>  	char *reset_name;
> -	struct mvebu_sw_pci_bridge bridge;
> +	struct pci_bridge_emul bridge;
>  	struct device_node *dn;
>  	struct mvebu_pcie *pcie;
>  	struct mvebu_pcie_window memwin;
> @@ -415,11 +361,12 @@ static void mvebu_pcie_set_window(struct mvebu_pcie_port *port,
>  static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
>  {
>  	struct mvebu_pcie_window desired = {};
> +	struct pci_bridge_emul_conf *conf = &port->bridge.conf;
>  
>  	/* Are the new iobase/iolimit values invalid? */
> -	if (port->bridge.iolimit < port->bridge.iobase ||
> -	    port->bridge.iolimitupper < port->bridge.iobaseupper ||
> -	    !(port->bridge.command & PCI_COMMAND_IO)) {
> +	if (conf->iolimit < conf->iobase ||
> +	    conf->iolimitupper < conf->iobaseupper ||
> +	    !(conf->command & PCI_COMMAND_IO)) {
>  		mvebu_pcie_set_window(port, port->io_target, port->io_attr,
>  				      &desired, &port->iowin);
>  		return;
> @@ -438,11 +385,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
>  	 * specifications. iobase is the bus address, port->iowin_base
>  	 * is the CPU address.
>  	 */
> -	desired.remap = ((port->bridge.iobase & 0xF0) << 8) |
> -			(port->bridge.iobaseupper << 16);
> +	desired.remap = ((conf->iobase & 0xF0) << 8) |
> +			(conf->iobaseupper << 16);
>  	desired.base = port->pcie->io.start + desired.remap;
> -	desired.size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) |
> -			 (port->bridge.iolimitupper << 16)) -
> +	desired.size = ((0xFFF | ((conf->iolimit & 0xF0) << 8) |
> +			 (conf->iolimitupper << 16)) -
>  			desired.remap) +
>  		       1;
>  
> @@ -453,10 +400,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
>  static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
>  {
>  	struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP};
> +	struct pci_bridge_emul_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;
> @@ -468,130 +416,34 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
>  	 * window to setup, according to the PCI-to-PCI bridge
>  	 * 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);
>  }
>  
> -/*
> - * Initialize the configuration space of the PCI-to-PCI bridge
> - * associated with the given PCIe interface.
> - */
> -static void mvebu_sw_pci_bridge_init(struct mvebu_pcie_port *port)
> +static pci_bridge_emul_read_status_t
> +mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
> +				     int reg, u32 *value)
>  {
> -	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
> -
> -	memset(bridge, 0, sizeof(struct mvebu_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;
> -}
> -
> -/*
> - * Read the configuration space of the PCI-to-PCI bridge associated to
> - * the given PCIe interface.
> - */
> -static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
> -				  unsigned int where, int size, u32 *value)
> -{
> -	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
> -
> -	switch (where & ~3) {
> -	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;
> -
> -	case PCI_MEMORY_BASE:
> -		*value = (bridge->memlimit << 16 | bridge->membase);
> -		break;
> -
> -	case PCI_PREF_MEMORY_BASE:
> -		*value = 0;
> -		break;
> -
> -	case PCI_IO_BASE_UPPER16:
> -		*value = (bridge->iolimitupper << 16 | bridge->iobaseupper);
> -		break;
> -
> -	case PCI_CAPABILITY_LIST:
> -		*value = PCISWCAP;
> -		break;
> +	struct mvebu_pcie_port *port = bridge->data;
>  
> -	case PCI_ROM_ADDRESS1:
> -		*value = 0;
> -		break;
> -
> -	case PCI_INTERRUPT_LINE:
> -		/* LINE PIN MIN_GNT MAX_LAT */
> -		*value = 0;
> -		break;
> -
> -	case PCISWCAP_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;
> -
> -	case PCISWCAP_EXP_DEVCAP:
> +	switch (reg) {
> +	case PCI_EXP_DEVCAP:
>  		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP);
>  		break;
>  
> -	case PCISWCAP_EXP_DEVCTL:
> +	case PCI_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;
>  
> -	case PCISWCAP_EXP_LNKCAP:
> +	case PCI_EXP_LNKCAP:
>  		/*
>  		 * PCIe requires the clock power management capability to be
>  		 * hard-wired to zero for downstream ports
> @@ -600,132 +452,88 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
>  			 ~PCI_EXP_LNKCAP_CLKPM;
>  		break;
>  
> -	case PCISWCAP_EXP_LNKCTL:
> +	case PCI_EXP_LNKCTL:
>  		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
>  		break;
>  
> -	case PCISWCAP_EXP_SLTCAP:
> -		*value = bridge->pcie_sltcap;
> -		break;
> -
> -	case PCISWCAP_EXP_SLTCTL:
> +	case PCI_EXP_SLTCTL:
>  		*value = PCI_EXP_SLTSTA_PDS << 16;
>  		break;
>  
> -	case PCISWCAP_EXP_RTCTL:
> -		*value = bridge->pcie_rtctl;
> -		break;
> -
> -	case PCISWCAP_EXP_RTSTA:
> +	case PCI_EXP_RTSTA:
>  		*value = mvebu_readl(port, PCIE_RC_RTSTA);
>  		break;
>  
> -	/* PCIe requires the v2 fields to be hard-wired to zero */
> -	case PCISWCAP_EXP_DEVCAP2:
> -	case PCISWCAP_EXP_DEVCTL2:
> -	case PCISWCAP_EXP_LNKCAP2:
> -	case PCISWCAP_EXP_LNKCTL2:
> -	case PCISWCAP_EXP_SLTCAP2:
> -	case PCISWCAP_EXP_SLTCTL2:
>  	default:
> -		/*
> -		 * PCI defines configuration read accesses to reserved or
> -		 * unimplemented registers to read as zero and complete
> -		 * normally.
> -		 */
> -		*value = 0;
> -		return PCIBIOS_SUCCESSFUL;
> +		return PCI_BRIDGE_EMUL_NOT_HANDLED;
>  	}
>  
> -	if (size == 2)
> -		*value = (*value >> (8 * (where & 3))) & 0xffff;
> -	else if (size == 1)
> -		*value = (*value >> (8 * (where & 3))) & 0xff;
> -
> -	return PCIBIOS_SUCCESSFUL;
> +	return PCI_BRIDGE_EMUL_HANDLED;
>  }
>  
> -/* Write to the PCI-to-PCI bridge configuration space */
> -static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
> -				     unsigned int where, int size, u32 value)
> +static void
> +mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge,
> +				      int reg, u32 old, u32 new, u32 mask)
>  {
> -	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
> -	u32 mask, reg;
> -	int err;
> -
> -	if (size == 4)
> -		mask = 0x0;
> -	else if (size == 2)
> -		mask = ~(0xffff << ((where & 3) * 8));
> -	else if (size == 1)
> -		mask = ~(0xff << ((where & 3) * 8));
> -	else
> -		return PCIBIOS_BAD_REGISTER_NUMBER;
> -
> -	err = mvebu_sw_pci_bridge_read(port, where & ~3, 4, &reg);
> -	if (err)
> -		return err;
> -
> -	value = (reg & mask) | value << ((where & 3) * 8);
> +	struct mvebu_pcie_port *port = bridge->data;
> +	struct pci_bridge_emul_conf *conf = &bridge->conf;
>  
> -	switch (where & ~3) {
> +	switch (reg) {
>  	case PCI_COMMAND:
>  	{
> -		u32 old = 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;
> -	}
>  
> -	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
> +		 * We 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;
> +		conf->iobase |= PCI_IO_RANGE_TYPE_32;
> +		conf->iolimit |= 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);
> +		mvebu_pcie_set_local_bus_nr(port, conf->secondary_bus);
>  		break;
>  
> -	case PCISWCAP_EXP_DEVCTL:
> +	default:
> +		break;
> +	}
> +}
> +
> +static void
> +mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
> +				      int reg, u32 old, u32 new, u32 mask)
> +{
> +	struct mvebu_pcie_port *port = bridge->data;
> +
> +	switch (reg) {
> +	case PCI_EXP_DEVCTL:
>  		/*
>  		 * Armada370 data says these bits must always
>  		 * be zero when in root complex mode.
>  		 */
> -		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);
>  
>  		/*
>  		 * If the mask is 0xffff0000, then we only want to write
> @@ -733,20 +541,20 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
>  		 * RW1C bits in the device status register.  Mask out the
>  		 * status register bits.
>  		 */
> -		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;
>  
> -	case PCISWCAP_EXP_LNKCTL:
> +	case PCI_EXP_LNKCTL:
>  		/*
>  		 * If we don't support CLKREQ, we must ensure that the
>  		 * CLKREQ enable bit always reads zero.  Since we haven't
>  		 * had this capability, and it's dependent on board wiring,
>  		 * disable it for the time being.
>  		 */
> -		value &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
> +		new &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
>  
>  		/*
>  		 * If the mask is 0xffff0000, then we only want to write
> @@ -755,21 +563,48 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
>  		 * RW1C status register bits.
>  		 */
>  		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;
>  
> -	case PCISWCAP_EXP_RTSTA:
> -		mvebu_writel(port, value, PCIE_RC_RTSTA);
> +	case PCI_EXP_RTSTA:
> +		mvebu_writel(port, new, PCIE_RC_RTSTA);
>  		break;
> +	}
> +}
>  
> -	default:
> -		break;
> +struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = {
> +	.write_base = mvebu_pci_bridge_emul_base_conf_write,
> +	.read_pcie = mvebu_pci_bridge_emul_pcie_conf_read,
> +	.write_pcie = mvebu_pci_bridge_emul_pcie_conf_write,
> +};
> +
> +/*
> + * Initialize the configuration space of the PCI-to-PCI bridge
> + * associated with the given PCIe interface.
> + */
> +static void mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
> +{
> +	struct pci_bridge_emul *bridge = &port->bridge;
> +
> +	bridge->conf.vendor = PCI_VENDOR_ID_MARVELL;
> +	bridge->conf.device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16;
> +	bridge->conf.class_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;
>  	}
>  
> -	return PCIBIOS_SUCCESSFUL;
> +	bridge->has_pcie = true;
> +	bridge->data = port;
> +	bridge->ops = &mvebu_pci_bridge_emul_ops;
> +
> +	pci_bridge_emul_init(bridge);
>  }
>  
>  static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys)
> @@ -789,8 +624,8 @@ static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
>  		if (bus->number == 0 && port->devfn == devfn)
>  			return port;
>  		if (bus->number != 0 &&
> -		    bus->number >= port->bridge.secondary_bus &&
> -		    bus->number <= port->bridge.subordinate_bus)
> +		    bus->number >= port->bridge.conf.secondary_bus &&
> +		    bus->number <= port->bridge.conf.subordinate_bus)
>  			return port;
>  	}
>  
> @@ -811,7 +646,8 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
>  
>  	/* Access the emulated PCI-to-PCI bridge */
>  	if (bus->number == 0)
> -		return mvebu_sw_pci_bridge_write(port, where, size, val);
> +		return pci_bridge_emul_conf_write(&port->bridge, where,
> +						  size, val);
>  
>  	if (!mvebu_pcie_link_up(port))
>  		return PCIBIOS_DEVICE_NOT_FOUND;
> @@ -839,7 +675,8 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
>  
>  	/* Access the emulated PCI-to-PCI bridge */
>  	if (bus->number == 0)
> -		return mvebu_sw_pci_bridge_read(port, where, size, val);
> +		return pci_bridge_emul_conf_read(&port->bridge, where,
> +						 size, val);
>  
>  	if (!mvebu_pcie_link_up(port)) {
>  		*val = 0xffffffff;
> @@ -1253,7 +1090,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
>  
>  		mvebu_pcie_setup_hw(port);
>  		mvebu_pcie_set_local_dev_nr(port, 1);
> -		mvebu_sw_pci_bridge_init(port);
> +		mvebu_pci_bridge_emul_init(port);
>  	}
>  
>  	pcie->nports = i;
> -- 
> 2.14.4
> 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCHv2 2/3] PCI: mvebu: Convert to PCI emulated bridge config space
@ 2018-09-18 16:17     ` Lorenzo Pieralisi
  0 siblings, 0 replies; 24+ messages in thread
From: Lorenzo Pieralisi @ 2018-09-18 16:17 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Sep 12, 2018 at 05:48:30PM +0200, Thomas Petazzoni wrote:
> This commit convers the pci-mvebu driver to use the recently

Nits:

s/convers/converts

Actually I would just write "Convert the pci-mvebu....".

I would drop "recently introduced", unless we can define a precise
commit when code was added so that it can actually be checked (I know
you can't since the relevant patch is part of this series and not in
the mainline yet).

Bjorn posted some guidelines that are helpful:

https://marc.info/?l=linux-pci&m=150905742808166&w=2

I can make these changes myself, no problem but please address Russell's
comment so that we can proceed.

Thanks,
Lorenzo

> introduced pci-bridge-emul logic, that helps emulating a root port PCI
> bridge configuration space.
> 
> 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 | 375 +++++++++++--------------------------
>  2 files changed, 107 insertions(+), 269 deletions(-)
> 
> diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
> index 028b287466fb..6d0f9930be7f 100644
> --- a/drivers/pci/controller/Kconfig
> +++ b/drivers/pci/controller/Kconfig
> @@ -9,6 +9,7 @@ config PCI_MVEBU
>  	depends on MVEBU_MBUS
>  	depends on ARM
>  	depends on OF
> +	select PCI_BRIDGE_EMUL
>  
>  config PCI_AARDVARK
>  	bool "Aardvark PCIe controller"
> diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
> index 50eb0729385b..f97f48cf2bf6 100644
> --- a/drivers/pci/controller/pci-mvebu.c
> +++ b/drivers/pci/controller/pci-mvebu.c
> @@ -22,6 +22,7 @@
>  #include <linux/of_platform.h>
>  
>  #include "../pci.h"
> +#include "../pci-bridge-emul.h"
>  
>  /*
>   * PCIe unit register offsets.
> @@ -63,61 +64,6 @@
>  #define PCIE_DEBUG_CTRL         0x1a60
>  #define  PCIE_DEBUG_SOFT_RESET		BIT(20)
>  
> -enum {
> -	PCISWCAP = PCI_BRIDGE_CONTROL + 2,
> -	PCISWCAP_EXP_LIST_ID	= PCISWCAP + PCI_CAP_LIST_ID,
> -	PCISWCAP_EXP_DEVCAP	= PCISWCAP + PCI_EXP_DEVCAP,
> -	PCISWCAP_EXP_DEVCTL	= PCISWCAP + PCI_EXP_DEVCTL,
> -	PCISWCAP_EXP_LNKCAP	= PCISWCAP + PCI_EXP_LNKCAP,
> -	PCISWCAP_EXP_LNKCTL	= PCISWCAP + PCI_EXP_LNKCTL,
> -	PCISWCAP_EXP_SLTCAP	= PCISWCAP + PCI_EXP_SLTCAP,
> -	PCISWCAP_EXP_SLTCTL	= PCISWCAP + PCI_EXP_SLTCTL,
> -	PCISWCAP_EXP_RTCTL	= PCISWCAP + PCI_EXP_RTCTL,
> -	PCISWCAP_EXP_RTSTA	= PCISWCAP + PCI_EXP_RTSTA,
> -	PCISWCAP_EXP_DEVCAP2	= PCISWCAP + PCI_EXP_DEVCAP2,
> -	PCISWCAP_EXP_DEVCTL2	= PCISWCAP + PCI_EXP_DEVCTL2,
> -	PCISWCAP_EXP_LNKCAP2	= PCISWCAP + PCI_EXP_LNKCAP2,
> -	PCISWCAP_EXP_LNKCTL2	= PCISWCAP + PCI_EXP_LNKCTL2,
> -	PCISWCAP_EXP_SLTCAP2	= PCISWCAP + PCI_EXP_SLTCAP2,
> -	PCISWCAP_EXP_SLTCTL2	= PCISWCAP + PCI_EXP_SLTCTL2,
> -};
> -
> -/* PCI configuration space of a PCI-to-PCI bridge */
> -struct mvebu_sw_pci_bridge {
> -	u16 vendor;
> -	u16 device;
> -	u16 command;
> -	u16 status;
> -	u16 class;
> -	u8 interface;
> -	u8 revision;
> -	u8 bist;
> -	u8 header_type;
> -	u8 latency_timer;
> -	u8 cache_line_size;
> -	u32 bar[2];
> -	u8 primary_bus;
> -	u8 secondary_bus;
> -	u8 subordinate_bus;
> -	u8 secondary_latency_timer;
> -	u8 iobase;
> -	u8 iolimit;
> -	u16 secondary_status;
> -	u16 membase;
> -	u16 memlimit;
> -	u16 iobaseupper;
> -	u16 iolimitupper;
> -	u32 romaddr;
> -	u8 intline;
> -	u8 intpin;
> -	u16 bridgectrl;
> -
> -	/* PCI express capability */
> -	u32 pcie_sltcap;
> -	u16 pcie_devctl;
> -	u16 pcie_rtctl;
> -};
> -
>  struct mvebu_pcie_port;
>  
>  /* Structure representing all PCIe interfaces */
> @@ -153,7 +99,7 @@ struct mvebu_pcie_port {
>  	struct clk *clk;
>  	struct gpio_desc *reset_gpio;
>  	char *reset_name;
> -	struct mvebu_sw_pci_bridge bridge;
> +	struct pci_bridge_emul bridge;
>  	struct device_node *dn;
>  	struct mvebu_pcie *pcie;
>  	struct mvebu_pcie_window memwin;
> @@ -415,11 +361,12 @@ static void mvebu_pcie_set_window(struct mvebu_pcie_port *port,
>  static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
>  {
>  	struct mvebu_pcie_window desired = {};
> +	struct pci_bridge_emul_conf *conf = &port->bridge.conf;
>  
>  	/* Are the new iobase/iolimit values invalid? */
> -	if (port->bridge.iolimit < port->bridge.iobase ||
> -	    port->bridge.iolimitupper < port->bridge.iobaseupper ||
> -	    !(port->bridge.command & PCI_COMMAND_IO)) {
> +	if (conf->iolimit < conf->iobase ||
> +	    conf->iolimitupper < conf->iobaseupper ||
> +	    !(conf->command & PCI_COMMAND_IO)) {
>  		mvebu_pcie_set_window(port, port->io_target, port->io_attr,
>  				      &desired, &port->iowin);
>  		return;
> @@ -438,11 +385,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
>  	 * specifications. iobase is the bus address, port->iowin_base
>  	 * is the CPU address.
>  	 */
> -	desired.remap = ((port->bridge.iobase & 0xF0) << 8) |
> -			(port->bridge.iobaseupper << 16);
> +	desired.remap = ((conf->iobase & 0xF0) << 8) |
> +			(conf->iobaseupper << 16);
>  	desired.base = port->pcie->io.start + desired.remap;
> -	desired.size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) |
> -			 (port->bridge.iolimitupper << 16)) -
> +	desired.size = ((0xFFF | ((conf->iolimit & 0xF0) << 8) |
> +			 (conf->iolimitupper << 16)) -
>  			desired.remap) +
>  		       1;
>  
> @@ -453,10 +400,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
>  static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
>  {
>  	struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP};
> +	struct pci_bridge_emul_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;
> @@ -468,130 +416,34 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
>  	 * window to setup, according to the PCI-to-PCI bridge
>  	 * 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);
>  }
>  
> -/*
> - * Initialize the configuration space of the PCI-to-PCI bridge
> - * associated with the given PCIe interface.
> - */
> -static void mvebu_sw_pci_bridge_init(struct mvebu_pcie_port *port)
> +static pci_bridge_emul_read_status_t
> +mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
> +				     int reg, u32 *value)
>  {
> -	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
> -
> -	memset(bridge, 0, sizeof(struct mvebu_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;
> -}
> -
> -/*
> - * Read the configuration space of the PCI-to-PCI bridge associated to
> - * the given PCIe interface.
> - */
> -static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
> -				  unsigned int where, int size, u32 *value)
> -{
> -	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
> -
> -	switch (where & ~3) {
> -	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;
> -
> -	case PCI_MEMORY_BASE:
> -		*value = (bridge->memlimit << 16 | bridge->membase);
> -		break;
> -
> -	case PCI_PREF_MEMORY_BASE:
> -		*value = 0;
> -		break;
> -
> -	case PCI_IO_BASE_UPPER16:
> -		*value = (bridge->iolimitupper << 16 | bridge->iobaseupper);
> -		break;
> -
> -	case PCI_CAPABILITY_LIST:
> -		*value = PCISWCAP;
> -		break;
> +	struct mvebu_pcie_port *port = bridge->data;
>  
> -	case PCI_ROM_ADDRESS1:
> -		*value = 0;
> -		break;
> -
> -	case PCI_INTERRUPT_LINE:
> -		/* LINE PIN MIN_GNT MAX_LAT */
> -		*value = 0;
> -		break;
> -
> -	case PCISWCAP_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;
> -
> -	case PCISWCAP_EXP_DEVCAP:
> +	switch (reg) {
> +	case PCI_EXP_DEVCAP:
>  		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP);
>  		break;
>  
> -	case PCISWCAP_EXP_DEVCTL:
> +	case PCI_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;
>  
> -	case PCISWCAP_EXP_LNKCAP:
> +	case PCI_EXP_LNKCAP:
>  		/*
>  		 * PCIe requires the clock power management capability to be
>  		 * hard-wired to zero for downstream ports
> @@ -600,132 +452,88 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
>  			 ~PCI_EXP_LNKCAP_CLKPM;
>  		break;
>  
> -	case PCISWCAP_EXP_LNKCTL:
> +	case PCI_EXP_LNKCTL:
>  		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
>  		break;
>  
> -	case PCISWCAP_EXP_SLTCAP:
> -		*value = bridge->pcie_sltcap;
> -		break;
> -
> -	case PCISWCAP_EXP_SLTCTL:
> +	case PCI_EXP_SLTCTL:
>  		*value = PCI_EXP_SLTSTA_PDS << 16;
>  		break;
>  
> -	case PCISWCAP_EXP_RTCTL:
> -		*value = bridge->pcie_rtctl;
> -		break;
> -
> -	case PCISWCAP_EXP_RTSTA:
> +	case PCI_EXP_RTSTA:
>  		*value = mvebu_readl(port, PCIE_RC_RTSTA);
>  		break;
>  
> -	/* PCIe requires the v2 fields to be hard-wired to zero */
> -	case PCISWCAP_EXP_DEVCAP2:
> -	case PCISWCAP_EXP_DEVCTL2:
> -	case PCISWCAP_EXP_LNKCAP2:
> -	case PCISWCAP_EXP_LNKCTL2:
> -	case PCISWCAP_EXP_SLTCAP2:
> -	case PCISWCAP_EXP_SLTCTL2:
>  	default:
> -		/*
> -		 * PCI defines configuration read accesses to reserved or
> -		 * unimplemented registers to read as zero and complete
> -		 * normally.
> -		 */
> -		*value = 0;
> -		return PCIBIOS_SUCCESSFUL;
> +		return PCI_BRIDGE_EMUL_NOT_HANDLED;
>  	}
>  
> -	if (size == 2)
> -		*value = (*value >> (8 * (where & 3))) & 0xffff;
> -	else if (size == 1)
> -		*value = (*value >> (8 * (where & 3))) & 0xff;
> -
> -	return PCIBIOS_SUCCESSFUL;
> +	return PCI_BRIDGE_EMUL_HANDLED;
>  }
>  
> -/* Write to the PCI-to-PCI bridge configuration space */
> -static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
> -				     unsigned int where, int size, u32 value)
> +static void
> +mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge,
> +				      int reg, u32 old, u32 new, u32 mask)
>  {
> -	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
> -	u32 mask, reg;
> -	int err;
> -
> -	if (size == 4)
> -		mask = 0x0;
> -	else if (size == 2)
> -		mask = ~(0xffff << ((where & 3) * 8));
> -	else if (size == 1)
> -		mask = ~(0xff << ((where & 3) * 8));
> -	else
> -		return PCIBIOS_BAD_REGISTER_NUMBER;
> -
> -	err = mvebu_sw_pci_bridge_read(port, where & ~3, 4, &reg);
> -	if (err)
> -		return err;
> -
> -	value = (reg & mask) | value << ((where & 3) * 8);
> +	struct mvebu_pcie_port *port = bridge->data;
> +	struct pci_bridge_emul_conf *conf = &bridge->conf;
>  
> -	switch (where & ~3) {
> +	switch (reg) {
>  	case PCI_COMMAND:
>  	{
> -		u32 old = 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;
> -	}
>  
> -	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
> +		 * We 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;
> +		conf->iobase |= PCI_IO_RANGE_TYPE_32;
> +		conf->iolimit |= 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);
> +		mvebu_pcie_set_local_bus_nr(port, conf->secondary_bus);
>  		break;
>  
> -	case PCISWCAP_EXP_DEVCTL:
> +	default:
> +		break;
> +	}
> +}
> +
> +static void
> +mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
> +				      int reg, u32 old, u32 new, u32 mask)
> +{
> +	struct mvebu_pcie_port *port = bridge->data;
> +
> +	switch (reg) {
> +	case PCI_EXP_DEVCTL:
>  		/*
>  		 * Armada370 data says these bits must always
>  		 * be zero when in root complex mode.
>  		 */
> -		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);
>  
>  		/*
>  		 * If the mask is 0xffff0000, then we only want to write
> @@ -733,20 +541,20 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
>  		 * RW1C bits in the device status register.  Mask out the
>  		 * status register bits.
>  		 */
> -		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;
>  
> -	case PCISWCAP_EXP_LNKCTL:
> +	case PCI_EXP_LNKCTL:
>  		/*
>  		 * If we don't support CLKREQ, we must ensure that the
>  		 * CLKREQ enable bit always reads zero.  Since we haven't
>  		 * had this capability, and it's dependent on board wiring,
>  		 * disable it for the time being.
>  		 */
> -		value &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
> +		new &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
>  
>  		/*
>  		 * If the mask is 0xffff0000, then we only want to write
> @@ -755,21 +563,48 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
>  		 * RW1C status register bits.
>  		 */
>  		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;
>  
> -	case PCISWCAP_EXP_RTSTA:
> -		mvebu_writel(port, value, PCIE_RC_RTSTA);
> +	case PCI_EXP_RTSTA:
> +		mvebu_writel(port, new, PCIE_RC_RTSTA);
>  		break;
> +	}
> +}
>  
> -	default:
> -		break;
> +struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = {
> +	.write_base = mvebu_pci_bridge_emul_base_conf_write,
> +	.read_pcie = mvebu_pci_bridge_emul_pcie_conf_read,
> +	.write_pcie = mvebu_pci_bridge_emul_pcie_conf_write,
> +};
> +
> +/*
> + * Initialize the configuration space of the PCI-to-PCI bridge
> + * associated with the given PCIe interface.
> + */
> +static void mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
> +{
> +	struct pci_bridge_emul *bridge = &port->bridge;
> +
> +	bridge->conf.vendor = PCI_VENDOR_ID_MARVELL;
> +	bridge->conf.device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16;
> +	bridge->conf.class_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;
>  	}
>  
> -	return PCIBIOS_SUCCESSFUL;
> +	bridge->has_pcie = true;
> +	bridge->data = port;
> +	bridge->ops = &mvebu_pci_bridge_emul_ops;
> +
> +	pci_bridge_emul_init(bridge);
>  }
>  
>  static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys)
> @@ -789,8 +624,8 @@ static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
>  		if (bus->number == 0 && port->devfn == devfn)
>  			return port;
>  		if (bus->number != 0 &&
> -		    bus->number >= port->bridge.secondary_bus &&
> -		    bus->number <= port->bridge.subordinate_bus)
> +		    bus->number >= port->bridge.conf.secondary_bus &&
> +		    bus->number <= port->bridge.conf.subordinate_bus)
>  			return port;
>  	}
>  
> @@ -811,7 +646,8 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
>  
>  	/* Access the emulated PCI-to-PCI bridge */
>  	if (bus->number == 0)
> -		return mvebu_sw_pci_bridge_write(port, where, size, val);
> +		return pci_bridge_emul_conf_write(&port->bridge, where,
> +						  size, val);
>  
>  	if (!mvebu_pcie_link_up(port))
>  		return PCIBIOS_DEVICE_NOT_FOUND;
> @@ -839,7 +675,8 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
>  
>  	/* Access the emulated PCI-to-PCI bridge */
>  	if (bus->number == 0)
> -		return mvebu_sw_pci_bridge_read(port, where, size, val);
> +		return pci_bridge_emul_conf_read(&port->bridge, where,
> +						 size, val);
>  
>  	if (!mvebu_pcie_link_up(port)) {
>  		*val = 0xffffffff;
> @@ -1253,7 +1090,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
>  
>  		mvebu_pcie_setup_hw(port);
>  		mvebu_pcie_set_local_dev_nr(port, 1);
> -		mvebu_sw_pci_bridge_init(port);
> +		mvebu_pci_bridge_emul_init(port);
>  	}
>  
>  	pcie->nports = i;
> -- 
> 2.14.4
> 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 2/3] PCI: mvebu: Convert to PCI emulated bridge config space
  2018-09-18 16:17     ` Lorenzo Pieralisi
@ 2018-10-04 10:48       ` Lorenzo Pieralisi
  -1 siblings, 0 replies; 24+ messages in thread
From: Lorenzo Pieralisi @ 2018-10-04 10:48 UTC (permalink / raw)
  To: Thomas Petazzoni
  Cc: Bjorn Helgaas, Russell King, linux-pci, linux-arm-kernel,
	Gregory Clement, Miqu??l Raynal, Maxime Chevallier,
	Antoine Tenart, Nadav Haklai

On Tue, Sep 18, 2018 at 05:17:45PM +0100, Lorenzo Pieralisi wrote:
> On Wed, Sep 12, 2018 at 05:48:30PM +0200, Thomas Petazzoni wrote:
> > This commit convers the pci-mvebu driver to use the recently
> 
> Nits:
> 
> s/convers/converts
> 
> Actually I would just write "Convert the pci-mvebu....".
> 
> I would drop "recently introduced", unless we can define a precise
> commit when code was added so that it can actually be checked (I know
> you can't since the relevant patch is part of this series and not in
> the mainline yet).
> 
> Bjorn posted some guidelines that are helpful:
> 
> https://marc.info/?l=linux-pci&m=150905742808166&w=2
> 
> I can make these changes myself, no problem but please address Russell's
> comment so that we can proceed.

Hi Thomas,

I understand you have more important things to think about these days :)
(congratulations), let me know if I can fix this patch up myself, I
would like to merge this series for v4.20.

Thanks,
Lorenzo

> 
> Thanks,
> Lorenzo
> 
> > introduced pci-bridge-emul logic, that helps emulating a root port PCI
> > bridge configuration space.
> > 
> > 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 | 375 +++++++++++--------------------------
> >  2 files changed, 107 insertions(+), 269 deletions(-)
> > 
> > diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
> > index 028b287466fb..6d0f9930be7f 100644
> > --- a/drivers/pci/controller/Kconfig
> > +++ b/drivers/pci/controller/Kconfig
> > @@ -9,6 +9,7 @@ config PCI_MVEBU
> >  	depends on MVEBU_MBUS
> >  	depends on ARM
> >  	depends on OF
> > +	select PCI_BRIDGE_EMUL
> >  
> >  config PCI_AARDVARK
> >  	bool "Aardvark PCIe controller"
> > diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
> > index 50eb0729385b..f97f48cf2bf6 100644
> > --- a/drivers/pci/controller/pci-mvebu.c
> > +++ b/drivers/pci/controller/pci-mvebu.c
> > @@ -22,6 +22,7 @@
> >  #include <linux/of_platform.h>
> >  
> >  #include "../pci.h"
> > +#include "../pci-bridge-emul.h"
> >  
> >  /*
> >   * PCIe unit register offsets.
> > @@ -63,61 +64,6 @@
> >  #define PCIE_DEBUG_CTRL         0x1a60
> >  #define  PCIE_DEBUG_SOFT_RESET		BIT(20)
> >  
> > -enum {
> > -	PCISWCAP = PCI_BRIDGE_CONTROL + 2,
> > -	PCISWCAP_EXP_LIST_ID	= PCISWCAP + PCI_CAP_LIST_ID,
> > -	PCISWCAP_EXP_DEVCAP	= PCISWCAP + PCI_EXP_DEVCAP,
> > -	PCISWCAP_EXP_DEVCTL	= PCISWCAP + PCI_EXP_DEVCTL,
> > -	PCISWCAP_EXP_LNKCAP	= PCISWCAP + PCI_EXP_LNKCAP,
> > -	PCISWCAP_EXP_LNKCTL	= PCISWCAP + PCI_EXP_LNKCTL,
> > -	PCISWCAP_EXP_SLTCAP	= PCISWCAP + PCI_EXP_SLTCAP,
> > -	PCISWCAP_EXP_SLTCTL	= PCISWCAP + PCI_EXP_SLTCTL,
> > -	PCISWCAP_EXP_RTCTL	= PCISWCAP + PCI_EXP_RTCTL,
> > -	PCISWCAP_EXP_RTSTA	= PCISWCAP + PCI_EXP_RTSTA,
> > -	PCISWCAP_EXP_DEVCAP2	= PCISWCAP + PCI_EXP_DEVCAP2,
> > -	PCISWCAP_EXP_DEVCTL2	= PCISWCAP + PCI_EXP_DEVCTL2,
> > -	PCISWCAP_EXP_LNKCAP2	= PCISWCAP + PCI_EXP_LNKCAP2,
> > -	PCISWCAP_EXP_LNKCTL2	= PCISWCAP + PCI_EXP_LNKCTL2,
> > -	PCISWCAP_EXP_SLTCAP2	= PCISWCAP + PCI_EXP_SLTCAP2,
> > -	PCISWCAP_EXP_SLTCTL2	= PCISWCAP + PCI_EXP_SLTCTL2,
> > -};
> > -
> > -/* PCI configuration space of a PCI-to-PCI bridge */
> > -struct mvebu_sw_pci_bridge {
> > -	u16 vendor;
> > -	u16 device;
> > -	u16 command;
> > -	u16 status;
> > -	u16 class;
> > -	u8 interface;
> > -	u8 revision;
> > -	u8 bist;
> > -	u8 header_type;
> > -	u8 latency_timer;
> > -	u8 cache_line_size;
> > -	u32 bar[2];
> > -	u8 primary_bus;
> > -	u8 secondary_bus;
> > -	u8 subordinate_bus;
> > -	u8 secondary_latency_timer;
> > -	u8 iobase;
> > -	u8 iolimit;
> > -	u16 secondary_status;
> > -	u16 membase;
> > -	u16 memlimit;
> > -	u16 iobaseupper;
> > -	u16 iolimitupper;
> > -	u32 romaddr;
> > -	u8 intline;
> > -	u8 intpin;
> > -	u16 bridgectrl;
> > -
> > -	/* PCI express capability */
> > -	u32 pcie_sltcap;
> > -	u16 pcie_devctl;
> > -	u16 pcie_rtctl;
> > -};
> > -
> >  struct mvebu_pcie_port;
> >  
> >  /* Structure representing all PCIe interfaces */
> > @@ -153,7 +99,7 @@ struct mvebu_pcie_port {
> >  	struct clk *clk;
> >  	struct gpio_desc *reset_gpio;
> >  	char *reset_name;
> > -	struct mvebu_sw_pci_bridge bridge;
> > +	struct pci_bridge_emul bridge;
> >  	struct device_node *dn;
> >  	struct mvebu_pcie *pcie;
> >  	struct mvebu_pcie_window memwin;
> > @@ -415,11 +361,12 @@ static void mvebu_pcie_set_window(struct mvebu_pcie_port *port,
> >  static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
> >  {
> >  	struct mvebu_pcie_window desired = {};
> > +	struct pci_bridge_emul_conf *conf = &port->bridge.conf;
> >  
> >  	/* Are the new iobase/iolimit values invalid? */
> > -	if (port->bridge.iolimit < port->bridge.iobase ||
> > -	    port->bridge.iolimitupper < port->bridge.iobaseupper ||
> > -	    !(port->bridge.command & PCI_COMMAND_IO)) {
> > +	if (conf->iolimit < conf->iobase ||
> > +	    conf->iolimitupper < conf->iobaseupper ||
> > +	    !(conf->command & PCI_COMMAND_IO)) {
> >  		mvebu_pcie_set_window(port, port->io_target, port->io_attr,
> >  				      &desired, &port->iowin);
> >  		return;
> > @@ -438,11 +385,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
> >  	 * specifications. iobase is the bus address, port->iowin_base
> >  	 * is the CPU address.
> >  	 */
> > -	desired.remap = ((port->bridge.iobase & 0xF0) << 8) |
> > -			(port->bridge.iobaseupper << 16);
> > +	desired.remap = ((conf->iobase & 0xF0) << 8) |
> > +			(conf->iobaseupper << 16);
> >  	desired.base = port->pcie->io.start + desired.remap;
> > -	desired.size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) |
> > -			 (port->bridge.iolimitupper << 16)) -
> > +	desired.size = ((0xFFF | ((conf->iolimit & 0xF0) << 8) |
> > +			 (conf->iolimitupper << 16)) -
> >  			desired.remap) +
> >  		       1;
> >  
> > @@ -453,10 +400,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
> >  static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
> >  {
> >  	struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP};
> > +	struct pci_bridge_emul_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;
> > @@ -468,130 +416,34 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
> >  	 * window to setup, according to the PCI-to-PCI bridge
> >  	 * 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);
> >  }
> >  
> > -/*
> > - * Initialize the configuration space of the PCI-to-PCI bridge
> > - * associated with the given PCIe interface.
> > - */
> > -static void mvebu_sw_pci_bridge_init(struct mvebu_pcie_port *port)
> > +static pci_bridge_emul_read_status_t
> > +mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
> > +				     int reg, u32 *value)
> >  {
> > -	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
> > -
> > -	memset(bridge, 0, sizeof(struct mvebu_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;
> > -}
> > -
> > -/*
> > - * Read the configuration space of the PCI-to-PCI bridge associated to
> > - * the given PCIe interface.
> > - */
> > -static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
> > -				  unsigned int where, int size, u32 *value)
> > -{
> > -	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
> > -
> > -	switch (where & ~3) {
> > -	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;
> > -
> > -	case PCI_MEMORY_BASE:
> > -		*value = (bridge->memlimit << 16 | bridge->membase);
> > -		break;
> > -
> > -	case PCI_PREF_MEMORY_BASE:
> > -		*value = 0;
> > -		break;
> > -
> > -	case PCI_IO_BASE_UPPER16:
> > -		*value = (bridge->iolimitupper << 16 | bridge->iobaseupper);
> > -		break;
> > -
> > -	case PCI_CAPABILITY_LIST:
> > -		*value = PCISWCAP;
> > -		break;
> > +	struct mvebu_pcie_port *port = bridge->data;
> >  
> > -	case PCI_ROM_ADDRESS1:
> > -		*value = 0;
> > -		break;
> > -
> > -	case PCI_INTERRUPT_LINE:
> > -		/* LINE PIN MIN_GNT MAX_LAT */
> > -		*value = 0;
> > -		break;
> > -
> > -	case PCISWCAP_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;
> > -
> > -	case PCISWCAP_EXP_DEVCAP:
> > +	switch (reg) {
> > +	case PCI_EXP_DEVCAP:
> >  		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP);
> >  		break;
> >  
> > -	case PCISWCAP_EXP_DEVCTL:
> > +	case PCI_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;
> >  
> > -	case PCISWCAP_EXP_LNKCAP:
> > +	case PCI_EXP_LNKCAP:
> >  		/*
> >  		 * PCIe requires the clock power management capability to be
> >  		 * hard-wired to zero for downstream ports
> > @@ -600,132 +452,88 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
> >  			 ~PCI_EXP_LNKCAP_CLKPM;
> >  		break;
> >  
> > -	case PCISWCAP_EXP_LNKCTL:
> > +	case PCI_EXP_LNKCTL:
> >  		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
> >  		break;
> >  
> > -	case PCISWCAP_EXP_SLTCAP:
> > -		*value = bridge->pcie_sltcap;
> > -		break;
> > -
> > -	case PCISWCAP_EXP_SLTCTL:
> > +	case PCI_EXP_SLTCTL:
> >  		*value = PCI_EXP_SLTSTA_PDS << 16;
> >  		break;
> >  
> > -	case PCISWCAP_EXP_RTCTL:
> > -		*value = bridge->pcie_rtctl;
> > -		break;
> > -
> > -	case PCISWCAP_EXP_RTSTA:
> > +	case PCI_EXP_RTSTA:
> >  		*value = mvebu_readl(port, PCIE_RC_RTSTA);
> >  		break;
> >  
> > -	/* PCIe requires the v2 fields to be hard-wired to zero */
> > -	case PCISWCAP_EXP_DEVCAP2:
> > -	case PCISWCAP_EXP_DEVCTL2:
> > -	case PCISWCAP_EXP_LNKCAP2:
> > -	case PCISWCAP_EXP_LNKCTL2:
> > -	case PCISWCAP_EXP_SLTCAP2:
> > -	case PCISWCAP_EXP_SLTCTL2:
> >  	default:
> > -		/*
> > -		 * PCI defines configuration read accesses to reserved or
> > -		 * unimplemented registers to read as zero and complete
> > -		 * normally.
> > -		 */
> > -		*value = 0;
> > -		return PCIBIOS_SUCCESSFUL;
> > +		return PCI_BRIDGE_EMUL_NOT_HANDLED;
> >  	}
> >  
> > -	if (size == 2)
> > -		*value = (*value >> (8 * (where & 3))) & 0xffff;
> > -	else if (size == 1)
> > -		*value = (*value >> (8 * (where & 3))) & 0xff;
> > -
> > -	return PCIBIOS_SUCCESSFUL;
> > +	return PCI_BRIDGE_EMUL_HANDLED;
> >  }
> >  
> > -/* Write to the PCI-to-PCI bridge configuration space */
> > -static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
> > -				     unsigned int where, int size, u32 value)
> > +static void
> > +mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge,
> > +				      int reg, u32 old, u32 new, u32 mask)
> >  {
> > -	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
> > -	u32 mask, reg;
> > -	int err;
> > -
> > -	if (size == 4)
> > -		mask = 0x0;
> > -	else if (size == 2)
> > -		mask = ~(0xffff << ((where & 3) * 8));
> > -	else if (size == 1)
> > -		mask = ~(0xff << ((where & 3) * 8));
> > -	else
> > -		return PCIBIOS_BAD_REGISTER_NUMBER;
> > -
> > -	err = mvebu_sw_pci_bridge_read(port, where & ~3, 4, &reg);
> > -	if (err)
> > -		return err;
> > -
> > -	value = (reg & mask) | value << ((where & 3) * 8);
> > +	struct mvebu_pcie_port *port = bridge->data;
> > +	struct pci_bridge_emul_conf *conf = &bridge->conf;
> >  
> > -	switch (where & ~3) {
> > +	switch (reg) {
> >  	case PCI_COMMAND:
> >  	{
> > -		u32 old = 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;
> > -	}
> >  
> > -	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
> > +		 * We 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;
> > +		conf->iobase |= PCI_IO_RANGE_TYPE_32;
> > +		conf->iolimit |= 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);
> > +		mvebu_pcie_set_local_bus_nr(port, conf->secondary_bus);
> >  		break;
> >  
> > -	case PCISWCAP_EXP_DEVCTL:
> > +	default:
> > +		break;
> > +	}
> > +}
> > +
> > +static void
> > +mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
> > +				      int reg, u32 old, u32 new, u32 mask)
> > +{
> > +	struct mvebu_pcie_port *port = bridge->data;
> > +
> > +	switch (reg) {
> > +	case PCI_EXP_DEVCTL:
> >  		/*
> >  		 * Armada370 data says these bits must always
> >  		 * be zero when in root complex mode.
> >  		 */
> > -		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);
> >  
> >  		/*
> >  		 * If the mask is 0xffff0000, then we only want to write
> > @@ -733,20 +541,20 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
> >  		 * RW1C bits in the device status register.  Mask out the
> >  		 * status register bits.
> >  		 */
> > -		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;
> >  
> > -	case PCISWCAP_EXP_LNKCTL:
> > +	case PCI_EXP_LNKCTL:
> >  		/*
> >  		 * If we don't support CLKREQ, we must ensure that the
> >  		 * CLKREQ enable bit always reads zero.  Since we haven't
> >  		 * had this capability, and it's dependent on board wiring,
> >  		 * disable it for the time being.
> >  		 */
> > -		value &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
> > +		new &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
> >  
> >  		/*
> >  		 * If the mask is 0xffff0000, then we only want to write
> > @@ -755,21 +563,48 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
> >  		 * RW1C status register bits.
> >  		 */
> >  		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;
> >  
> > -	case PCISWCAP_EXP_RTSTA:
> > -		mvebu_writel(port, value, PCIE_RC_RTSTA);
> > +	case PCI_EXP_RTSTA:
> > +		mvebu_writel(port, new, PCIE_RC_RTSTA);
> >  		break;
> > +	}
> > +}
> >  
> > -	default:
> > -		break;
> > +struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = {
> > +	.write_base = mvebu_pci_bridge_emul_base_conf_write,
> > +	.read_pcie = mvebu_pci_bridge_emul_pcie_conf_read,
> > +	.write_pcie = mvebu_pci_bridge_emul_pcie_conf_write,
> > +};
> > +
> > +/*
> > + * Initialize the configuration space of the PCI-to-PCI bridge
> > + * associated with the given PCIe interface.
> > + */
> > +static void mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
> > +{
> > +	struct pci_bridge_emul *bridge = &port->bridge;
> > +
> > +	bridge->conf.vendor = PCI_VENDOR_ID_MARVELL;
> > +	bridge->conf.device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16;
> > +	bridge->conf.class_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;
> >  	}
> >  
> > -	return PCIBIOS_SUCCESSFUL;
> > +	bridge->has_pcie = true;
> > +	bridge->data = port;
> > +	bridge->ops = &mvebu_pci_bridge_emul_ops;
> > +
> > +	pci_bridge_emul_init(bridge);
> >  }
> >  
> >  static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys)
> > @@ -789,8 +624,8 @@ static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
> >  		if (bus->number == 0 && port->devfn == devfn)
> >  			return port;
> >  		if (bus->number != 0 &&
> > -		    bus->number >= port->bridge.secondary_bus &&
> > -		    bus->number <= port->bridge.subordinate_bus)
> > +		    bus->number >= port->bridge.conf.secondary_bus &&
> > +		    bus->number <= port->bridge.conf.subordinate_bus)
> >  			return port;
> >  	}
> >  
> > @@ -811,7 +646,8 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
> >  
> >  	/* Access the emulated PCI-to-PCI bridge */
> >  	if (bus->number == 0)
> > -		return mvebu_sw_pci_bridge_write(port, where, size, val);
> > +		return pci_bridge_emul_conf_write(&port->bridge, where,
> > +						  size, val);
> >  
> >  	if (!mvebu_pcie_link_up(port))
> >  		return PCIBIOS_DEVICE_NOT_FOUND;
> > @@ -839,7 +675,8 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
> >  
> >  	/* Access the emulated PCI-to-PCI bridge */
> >  	if (bus->number == 0)
> > -		return mvebu_sw_pci_bridge_read(port, where, size, val);
> > +		return pci_bridge_emul_conf_read(&port->bridge, where,
> > +						 size, val);
> >  
> >  	if (!mvebu_pcie_link_up(port)) {
> >  		*val = 0xffffffff;
> > @@ -1253,7 +1090,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
> >  
> >  		mvebu_pcie_setup_hw(port);
> >  		mvebu_pcie_set_local_dev_nr(port, 1);
> > -		mvebu_sw_pci_bridge_init(port);
> > +		mvebu_pci_bridge_emul_init(port);
> >  	}
> >  
> >  	pcie->nports = i;
> > -- 
> > 2.14.4
> > 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCHv2 2/3] PCI: mvebu: Convert to PCI emulated bridge config space
@ 2018-10-04 10:48       ` Lorenzo Pieralisi
  0 siblings, 0 replies; 24+ messages in thread
From: Lorenzo Pieralisi @ 2018-10-04 10:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Sep 18, 2018 at 05:17:45PM +0100, Lorenzo Pieralisi wrote:
> On Wed, Sep 12, 2018 at 05:48:30PM +0200, Thomas Petazzoni wrote:
> > This commit convers the pci-mvebu driver to use the recently
> 
> Nits:
> 
> s/convers/converts
> 
> Actually I would just write "Convert the pci-mvebu....".
> 
> I would drop "recently introduced", unless we can define a precise
> commit when code was added so that it can actually be checked (I know
> you can't since the relevant patch is part of this series and not in
> the mainline yet).
> 
> Bjorn posted some guidelines that are helpful:
> 
> https://marc.info/?l=linux-pci&m=150905742808166&w=2
> 
> I can make these changes myself, no problem but please address Russell's
> comment so that we can proceed.

Hi Thomas,

I understand you have more important things to think about these days :)
(congratulations), let me know if I can fix this patch up myself, I
would like to merge this series for v4.20.

Thanks,
Lorenzo

> 
> Thanks,
> Lorenzo
> 
> > introduced pci-bridge-emul logic, that helps emulating a root port PCI
> > bridge configuration space.
> > 
> > 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 | 375 +++++++++++--------------------------
> >  2 files changed, 107 insertions(+), 269 deletions(-)
> > 
> > diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
> > index 028b287466fb..6d0f9930be7f 100644
> > --- a/drivers/pci/controller/Kconfig
> > +++ b/drivers/pci/controller/Kconfig
> > @@ -9,6 +9,7 @@ config PCI_MVEBU
> >  	depends on MVEBU_MBUS
> >  	depends on ARM
> >  	depends on OF
> > +	select PCI_BRIDGE_EMUL
> >  
> >  config PCI_AARDVARK
> >  	bool "Aardvark PCIe controller"
> > diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
> > index 50eb0729385b..f97f48cf2bf6 100644
> > --- a/drivers/pci/controller/pci-mvebu.c
> > +++ b/drivers/pci/controller/pci-mvebu.c
> > @@ -22,6 +22,7 @@
> >  #include <linux/of_platform.h>
> >  
> >  #include "../pci.h"
> > +#include "../pci-bridge-emul.h"
> >  
> >  /*
> >   * PCIe unit register offsets.
> > @@ -63,61 +64,6 @@
> >  #define PCIE_DEBUG_CTRL         0x1a60
> >  #define  PCIE_DEBUG_SOFT_RESET		BIT(20)
> >  
> > -enum {
> > -	PCISWCAP = PCI_BRIDGE_CONTROL + 2,
> > -	PCISWCAP_EXP_LIST_ID	= PCISWCAP + PCI_CAP_LIST_ID,
> > -	PCISWCAP_EXP_DEVCAP	= PCISWCAP + PCI_EXP_DEVCAP,
> > -	PCISWCAP_EXP_DEVCTL	= PCISWCAP + PCI_EXP_DEVCTL,
> > -	PCISWCAP_EXP_LNKCAP	= PCISWCAP + PCI_EXP_LNKCAP,
> > -	PCISWCAP_EXP_LNKCTL	= PCISWCAP + PCI_EXP_LNKCTL,
> > -	PCISWCAP_EXP_SLTCAP	= PCISWCAP + PCI_EXP_SLTCAP,
> > -	PCISWCAP_EXP_SLTCTL	= PCISWCAP + PCI_EXP_SLTCTL,
> > -	PCISWCAP_EXP_RTCTL	= PCISWCAP + PCI_EXP_RTCTL,
> > -	PCISWCAP_EXP_RTSTA	= PCISWCAP + PCI_EXP_RTSTA,
> > -	PCISWCAP_EXP_DEVCAP2	= PCISWCAP + PCI_EXP_DEVCAP2,
> > -	PCISWCAP_EXP_DEVCTL2	= PCISWCAP + PCI_EXP_DEVCTL2,
> > -	PCISWCAP_EXP_LNKCAP2	= PCISWCAP + PCI_EXP_LNKCAP2,
> > -	PCISWCAP_EXP_LNKCTL2	= PCISWCAP + PCI_EXP_LNKCTL2,
> > -	PCISWCAP_EXP_SLTCAP2	= PCISWCAP + PCI_EXP_SLTCAP2,
> > -	PCISWCAP_EXP_SLTCTL2	= PCISWCAP + PCI_EXP_SLTCTL2,
> > -};
> > -
> > -/* PCI configuration space of a PCI-to-PCI bridge */
> > -struct mvebu_sw_pci_bridge {
> > -	u16 vendor;
> > -	u16 device;
> > -	u16 command;
> > -	u16 status;
> > -	u16 class;
> > -	u8 interface;
> > -	u8 revision;
> > -	u8 bist;
> > -	u8 header_type;
> > -	u8 latency_timer;
> > -	u8 cache_line_size;
> > -	u32 bar[2];
> > -	u8 primary_bus;
> > -	u8 secondary_bus;
> > -	u8 subordinate_bus;
> > -	u8 secondary_latency_timer;
> > -	u8 iobase;
> > -	u8 iolimit;
> > -	u16 secondary_status;
> > -	u16 membase;
> > -	u16 memlimit;
> > -	u16 iobaseupper;
> > -	u16 iolimitupper;
> > -	u32 romaddr;
> > -	u8 intline;
> > -	u8 intpin;
> > -	u16 bridgectrl;
> > -
> > -	/* PCI express capability */
> > -	u32 pcie_sltcap;
> > -	u16 pcie_devctl;
> > -	u16 pcie_rtctl;
> > -};
> > -
> >  struct mvebu_pcie_port;
> >  
> >  /* Structure representing all PCIe interfaces */
> > @@ -153,7 +99,7 @@ struct mvebu_pcie_port {
> >  	struct clk *clk;
> >  	struct gpio_desc *reset_gpio;
> >  	char *reset_name;
> > -	struct mvebu_sw_pci_bridge bridge;
> > +	struct pci_bridge_emul bridge;
> >  	struct device_node *dn;
> >  	struct mvebu_pcie *pcie;
> >  	struct mvebu_pcie_window memwin;
> > @@ -415,11 +361,12 @@ static void mvebu_pcie_set_window(struct mvebu_pcie_port *port,
> >  static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
> >  {
> >  	struct mvebu_pcie_window desired = {};
> > +	struct pci_bridge_emul_conf *conf = &port->bridge.conf;
> >  
> >  	/* Are the new iobase/iolimit values invalid? */
> > -	if (port->bridge.iolimit < port->bridge.iobase ||
> > -	    port->bridge.iolimitupper < port->bridge.iobaseupper ||
> > -	    !(port->bridge.command & PCI_COMMAND_IO)) {
> > +	if (conf->iolimit < conf->iobase ||
> > +	    conf->iolimitupper < conf->iobaseupper ||
> > +	    !(conf->command & PCI_COMMAND_IO)) {
> >  		mvebu_pcie_set_window(port, port->io_target, port->io_attr,
> >  				      &desired, &port->iowin);
> >  		return;
> > @@ -438,11 +385,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
> >  	 * specifications. iobase is the bus address, port->iowin_base
> >  	 * is the CPU address.
> >  	 */
> > -	desired.remap = ((port->bridge.iobase & 0xF0) << 8) |
> > -			(port->bridge.iobaseupper << 16);
> > +	desired.remap = ((conf->iobase & 0xF0) << 8) |
> > +			(conf->iobaseupper << 16);
> >  	desired.base = port->pcie->io.start + desired.remap;
> > -	desired.size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) |
> > -			 (port->bridge.iolimitupper << 16)) -
> > +	desired.size = ((0xFFF | ((conf->iolimit & 0xF0) << 8) |
> > +			 (conf->iolimitupper << 16)) -
> >  			desired.remap) +
> >  		       1;
> >  
> > @@ -453,10 +400,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
> >  static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
> >  {
> >  	struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP};
> > +	struct pci_bridge_emul_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;
> > @@ -468,130 +416,34 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
> >  	 * window to setup, according to the PCI-to-PCI bridge
> >  	 * 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);
> >  }
> >  
> > -/*
> > - * Initialize the configuration space of the PCI-to-PCI bridge
> > - * associated with the given PCIe interface.
> > - */
> > -static void mvebu_sw_pci_bridge_init(struct mvebu_pcie_port *port)
> > +static pci_bridge_emul_read_status_t
> > +mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
> > +				     int reg, u32 *value)
> >  {
> > -	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
> > -
> > -	memset(bridge, 0, sizeof(struct mvebu_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;
> > -}
> > -
> > -/*
> > - * Read the configuration space of the PCI-to-PCI bridge associated to
> > - * the given PCIe interface.
> > - */
> > -static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
> > -				  unsigned int where, int size, u32 *value)
> > -{
> > -	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
> > -
> > -	switch (where & ~3) {
> > -	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;
> > -
> > -	case PCI_MEMORY_BASE:
> > -		*value = (bridge->memlimit << 16 | bridge->membase);
> > -		break;
> > -
> > -	case PCI_PREF_MEMORY_BASE:
> > -		*value = 0;
> > -		break;
> > -
> > -	case PCI_IO_BASE_UPPER16:
> > -		*value = (bridge->iolimitupper << 16 | bridge->iobaseupper);
> > -		break;
> > -
> > -	case PCI_CAPABILITY_LIST:
> > -		*value = PCISWCAP;
> > -		break;
> > +	struct mvebu_pcie_port *port = bridge->data;
> >  
> > -	case PCI_ROM_ADDRESS1:
> > -		*value = 0;
> > -		break;
> > -
> > -	case PCI_INTERRUPT_LINE:
> > -		/* LINE PIN MIN_GNT MAX_LAT */
> > -		*value = 0;
> > -		break;
> > -
> > -	case PCISWCAP_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;
> > -
> > -	case PCISWCAP_EXP_DEVCAP:
> > +	switch (reg) {
> > +	case PCI_EXP_DEVCAP:
> >  		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP);
> >  		break;
> >  
> > -	case PCISWCAP_EXP_DEVCTL:
> > +	case PCI_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;
> >  
> > -	case PCISWCAP_EXP_LNKCAP:
> > +	case PCI_EXP_LNKCAP:
> >  		/*
> >  		 * PCIe requires the clock power management capability to be
> >  		 * hard-wired to zero for downstream ports
> > @@ -600,132 +452,88 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
> >  			 ~PCI_EXP_LNKCAP_CLKPM;
> >  		break;
> >  
> > -	case PCISWCAP_EXP_LNKCTL:
> > +	case PCI_EXP_LNKCTL:
> >  		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
> >  		break;
> >  
> > -	case PCISWCAP_EXP_SLTCAP:
> > -		*value = bridge->pcie_sltcap;
> > -		break;
> > -
> > -	case PCISWCAP_EXP_SLTCTL:
> > +	case PCI_EXP_SLTCTL:
> >  		*value = PCI_EXP_SLTSTA_PDS << 16;
> >  		break;
> >  
> > -	case PCISWCAP_EXP_RTCTL:
> > -		*value = bridge->pcie_rtctl;
> > -		break;
> > -
> > -	case PCISWCAP_EXP_RTSTA:
> > +	case PCI_EXP_RTSTA:
> >  		*value = mvebu_readl(port, PCIE_RC_RTSTA);
> >  		break;
> >  
> > -	/* PCIe requires the v2 fields to be hard-wired to zero */
> > -	case PCISWCAP_EXP_DEVCAP2:
> > -	case PCISWCAP_EXP_DEVCTL2:
> > -	case PCISWCAP_EXP_LNKCAP2:
> > -	case PCISWCAP_EXP_LNKCTL2:
> > -	case PCISWCAP_EXP_SLTCAP2:
> > -	case PCISWCAP_EXP_SLTCTL2:
> >  	default:
> > -		/*
> > -		 * PCI defines configuration read accesses to reserved or
> > -		 * unimplemented registers to read as zero and complete
> > -		 * normally.
> > -		 */
> > -		*value = 0;
> > -		return PCIBIOS_SUCCESSFUL;
> > +		return PCI_BRIDGE_EMUL_NOT_HANDLED;
> >  	}
> >  
> > -	if (size == 2)
> > -		*value = (*value >> (8 * (where & 3))) & 0xffff;
> > -	else if (size == 1)
> > -		*value = (*value >> (8 * (where & 3))) & 0xff;
> > -
> > -	return PCIBIOS_SUCCESSFUL;
> > +	return PCI_BRIDGE_EMUL_HANDLED;
> >  }
> >  
> > -/* Write to the PCI-to-PCI bridge configuration space */
> > -static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
> > -				     unsigned int where, int size, u32 value)
> > +static void
> > +mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge,
> > +				      int reg, u32 old, u32 new, u32 mask)
> >  {
> > -	struct mvebu_sw_pci_bridge *bridge = &port->bridge;
> > -	u32 mask, reg;
> > -	int err;
> > -
> > -	if (size == 4)
> > -		mask = 0x0;
> > -	else if (size == 2)
> > -		mask = ~(0xffff << ((where & 3) * 8));
> > -	else if (size == 1)
> > -		mask = ~(0xff << ((where & 3) * 8));
> > -	else
> > -		return PCIBIOS_BAD_REGISTER_NUMBER;
> > -
> > -	err = mvebu_sw_pci_bridge_read(port, where & ~3, 4, &reg);
> > -	if (err)
> > -		return err;
> > -
> > -	value = (reg & mask) | value << ((where & 3) * 8);
> > +	struct mvebu_pcie_port *port = bridge->data;
> > +	struct pci_bridge_emul_conf *conf = &bridge->conf;
> >  
> > -	switch (where & ~3) {
> > +	switch (reg) {
> >  	case PCI_COMMAND:
> >  	{
> > -		u32 old = 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;
> > -	}
> >  
> > -	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
> > +		 * We 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;
> > +		conf->iobase |= PCI_IO_RANGE_TYPE_32;
> > +		conf->iolimit |= 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);
> > +		mvebu_pcie_set_local_bus_nr(port, conf->secondary_bus);
> >  		break;
> >  
> > -	case PCISWCAP_EXP_DEVCTL:
> > +	default:
> > +		break;
> > +	}
> > +}
> > +
> > +static void
> > +mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
> > +				      int reg, u32 old, u32 new, u32 mask)
> > +{
> > +	struct mvebu_pcie_port *port = bridge->data;
> > +
> > +	switch (reg) {
> > +	case PCI_EXP_DEVCTL:
> >  		/*
> >  		 * Armada370 data says these bits must always
> >  		 * be zero when in root complex mode.
> >  		 */
> > -		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);
> >  
> >  		/*
> >  		 * If the mask is 0xffff0000, then we only want to write
> > @@ -733,20 +541,20 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
> >  		 * RW1C bits in the device status register.  Mask out the
> >  		 * status register bits.
> >  		 */
> > -		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;
> >  
> > -	case PCISWCAP_EXP_LNKCTL:
> > +	case PCI_EXP_LNKCTL:
> >  		/*
> >  		 * If we don't support CLKREQ, we must ensure that the
> >  		 * CLKREQ enable bit always reads zero.  Since we haven't
> >  		 * had this capability, and it's dependent on board wiring,
> >  		 * disable it for the time being.
> >  		 */
> > -		value &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
> > +		new &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
> >  
> >  		/*
> >  		 * If the mask is 0xffff0000, then we only want to write
> > @@ -755,21 +563,48 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
> >  		 * RW1C status register bits.
> >  		 */
> >  		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;
> >  
> > -	case PCISWCAP_EXP_RTSTA:
> > -		mvebu_writel(port, value, PCIE_RC_RTSTA);
> > +	case PCI_EXP_RTSTA:
> > +		mvebu_writel(port, new, PCIE_RC_RTSTA);
> >  		break;
> > +	}
> > +}
> >  
> > -	default:
> > -		break;
> > +struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = {
> > +	.write_base = mvebu_pci_bridge_emul_base_conf_write,
> > +	.read_pcie = mvebu_pci_bridge_emul_pcie_conf_read,
> > +	.write_pcie = mvebu_pci_bridge_emul_pcie_conf_write,
> > +};
> > +
> > +/*
> > + * Initialize the configuration space of the PCI-to-PCI bridge
> > + * associated with the given PCIe interface.
> > + */
> > +static void mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
> > +{
> > +	struct pci_bridge_emul *bridge = &port->bridge;
> > +
> > +	bridge->conf.vendor = PCI_VENDOR_ID_MARVELL;
> > +	bridge->conf.device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16;
> > +	bridge->conf.class_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;
> >  	}
> >  
> > -	return PCIBIOS_SUCCESSFUL;
> > +	bridge->has_pcie = true;
> > +	bridge->data = port;
> > +	bridge->ops = &mvebu_pci_bridge_emul_ops;
> > +
> > +	pci_bridge_emul_init(bridge);
> >  }
> >  
> >  static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys)
> > @@ -789,8 +624,8 @@ static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
> >  		if (bus->number == 0 && port->devfn == devfn)
> >  			return port;
> >  		if (bus->number != 0 &&
> > -		    bus->number >= port->bridge.secondary_bus &&
> > -		    bus->number <= port->bridge.subordinate_bus)
> > +		    bus->number >= port->bridge.conf.secondary_bus &&
> > +		    bus->number <= port->bridge.conf.subordinate_bus)
> >  			return port;
> >  	}
> >  
> > @@ -811,7 +646,8 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
> >  
> >  	/* Access the emulated PCI-to-PCI bridge */
> >  	if (bus->number == 0)
> > -		return mvebu_sw_pci_bridge_write(port, where, size, val);
> > +		return pci_bridge_emul_conf_write(&port->bridge, where,
> > +						  size, val);
> >  
> >  	if (!mvebu_pcie_link_up(port))
> >  		return PCIBIOS_DEVICE_NOT_FOUND;
> > @@ -839,7 +675,8 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
> >  
> >  	/* Access the emulated PCI-to-PCI bridge */
> >  	if (bus->number == 0)
> > -		return mvebu_sw_pci_bridge_read(port, where, size, val);
> > +		return pci_bridge_emul_conf_read(&port->bridge, where,
> > +						 size, val);
> >  
> >  	if (!mvebu_pcie_link_up(port)) {
> >  		*val = 0xffffffff;
> > @@ -1253,7 +1090,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
> >  
> >  		mvebu_pcie_setup_hw(port);
> >  		mvebu_pcie_set_local_dev_nr(port, 1);
> > -		mvebu_sw_pci_bridge_init(port);
> > +		mvebu_pci_bridge_emul_init(port);
> >  	}
> >  
> >  	pcie->nports = i;
> > -- 
> > 2.14.4
> > 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 2/3] PCI: mvebu: Convert to PCI emulated bridge config space
  2018-10-04 10:48       ` Lorenzo Pieralisi
@ 2018-10-04 11:13         ` Thomas Petazzoni
  -1 siblings, 0 replies; 24+ messages in thread
From: Thomas Petazzoni @ 2018-10-04 11:13 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Bjorn Helgaas, Russell King, linux-pci, linux-arm-kernel,
	Gregory Clement, Miqu??l Raynal, Maxime Chevallier,
	Antoine Tenart, Nadav Haklai

Hello,

On Thu, 4 Oct 2018 11:48:05 +0100, Lorenzo Pieralisi wrote:

> > I would drop "recently introduced", unless we can define a precise
> > commit when code was added so that it can actually be checked (I know
> > you can't since the relevant patch is part of this series and not in
> > the mainline yet).
> > 
> > Bjorn posted some guidelines that are helpful:
> > 
> > https://marc.info/?l=linux-pci&m=150905742808166&w=2
> > 
> > I can make these changes myself, no problem but please address Russell's
> > comment so that we can proceed.  
> 
> Hi Thomas,
> 
> I understand you have more important things to think about these days :)
> (congratulations), let me know if I can fix this patch up myself, I
> would like to merge this series for v4.20.

Fixing up the commit log, of course, you can fix up yourself. However,
Russell made a comment about a remaining FIXME, and I haven't had the
chance to get back deep into the code to remember why I added this
FIXME, and whether it is still needed or not.

Best regards,

Thomas
-- 
Thomas Petazzoni, CTO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCHv2 2/3] PCI: mvebu: Convert to PCI emulated bridge config space
@ 2018-10-04 11:13         ` Thomas Petazzoni
  0 siblings, 0 replies; 24+ messages in thread
From: Thomas Petazzoni @ 2018-10-04 11:13 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

On Thu, 4 Oct 2018 11:48:05 +0100, Lorenzo Pieralisi wrote:

> > I would drop "recently introduced", unless we can define a precise
> > commit when code was added so that it can actually be checked (I know
> > you can't since the relevant patch is part of this series and not in
> > the mainline yet).
> > 
> > Bjorn posted some guidelines that are helpful:
> > 
> > https://marc.info/?l=linux-pci&m=150905742808166&w=2
> > 
> > I can make these changes myself, no problem but please address Russell's
> > comment so that we can proceed.  
> 
> Hi Thomas,
> 
> I understand you have more important things to think about these days :)
> (congratulations), let me know if I can fix this patch up myself, I
> would like to merge this series for v4.20.

Fixing up the commit log, of course, you can fix up yourself. However,
Russell made a comment about a remaining FIXME, and I haven't had the
chance to get back deep into the code to remember why I added this
FIXME, and whether it is still needed or not.

Best regards,

Thomas
-- 
Thomas Petazzoni, CTO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCHv2 2/3] PCI: mvebu: Convert to PCI emulated bridge config space
  2018-10-04 11:13         ` Thomas Petazzoni
@ 2018-10-04 11:26           ` Russell King - ARM Linux
  -1 siblings, 0 replies; 24+ messages in thread
From: Russell King - ARM Linux @ 2018-10-04 11:26 UTC (permalink / raw)
  To: Thomas Petazzoni
  Cc: Lorenzo Pieralisi, Bjorn Helgaas, linux-pci, linux-arm-kernel,
	Gregory Clement, Miqu??l Raynal, Maxime Chevallier,
	Antoine Tenart, Nadav Haklai

On Thu, Oct 04, 2018 at 01:13:42PM +0200, Thomas Petazzoni wrote:
> Hello,
> 
> On Thu, 4 Oct 2018 11:48:05 +0100, Lorenzo Pieralisi wrote:
> 
> > > I would drop "recently introduced", unless we can define a precise
> > > commit when code was added so that it can actually be checked (I know
> > > you can't since the relevant patch is part of this series and not in
> > > the mainline yet).
> > > 
> > > Bjorn posted some guidelines that are helpful:
> > > 
> > > https://marc.info/?l=linux-pci&m=150905742808166&w=2
> > > 
> > > I can make these changes myself, no problem but please address Russell's
> > > comment so that we can proceed.  
> > 
> > Hi Thomas,
> > 
> > I understand you have more important things to think about these days :)
> > (congratulations), let me know if I can fix this patch up myself, I
> > would like to merge this series for v4.20.
> 
> Fixing up the commit log, of course, you can fix up yourself. However,
> Russell made a comment about a remaining FIXME, and I haven't had the
> chance to get back deep into the code to remember why I added this
> FIXME, and whether it is still needed or not.

Thank you for making my point about unexplained FIXME comments so well!

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 12.1Mbps down 622kbps up
According to speedtest.net: 11.9Mbps down 500kbps up

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCHv2 2/3] PCI: mvebu: Convert to PCI emulated bridge config space
@ 2018-10-04 11:26           ` Russell King - ARM Linux
  0 siblings, 0 replies; 24+ messages in thread
From: Russell King - ARM Linux @ 2018-10-04 11:26 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Oct 04, 2018 at 01:13:42PM +0200, Thomas Petazzoni wrote:
> Hello,
> 
> On Thu, 4 Oct 2018 11:48:05 +0100, Lorenzo Pieralisi wrote:
> 
> > > I would drop "recently introduced", unless we can define a precise
> > > commit when code was added so that it can actually be checked (I know
> > > you can't since the relevant patch is part of this series and not in
> > > the mainline yet).
> > > 
> > > Bjorn posted some guidelines that are helpful:
> > > 
> > > https://marc.info/?l=linux-pci&m=150905742808166&w=2
> > > 
> > > I can make these changes myself, no problem but please address Russell's
> > > comment so that we can proceed.  
> > 
> > Hi Thomas,
> > 
> > I understand you have more important things to think about these days :)
> > (congratulations), let me know if I can fix this patch up myself, I
> > would like to merge this series for v4.20.
> 
> Fixing up the commit log, of course, you can fix up yourself. However,
> Russell made a comment about a remaining FIXME, and I haven't had the
> chance to get back deep into the code to remember why I added this
> FIXME, and whether it is still needed or not.

Thank you for making my point about unexplained FIXME comments so well!

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 12.1Mbps down 622kbps up
According to speedtest.net: 11.9Mbps down 500kbps up

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2018-10-04 11:27 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-12 15:48 [PATCHv2 0/3] PCI: emulated PCI bridge config space Thomas Petazzoni
2018-09-12 15:48 ` Thomas Petazzoni
2018-09-12 15:48 ` [PATCHv2 1/3] PCI: Introduce PCI bridge emulated config space common logic Thomas Petazzoni
2018-09-12 15:48   ` Thomas Petazzoni
2018-09-12 18:53   ` Bjorn Helgaas
2018-09-12 18:53     ` Bjorn Helgaas
2018-09-14 14:38   ` Russell King - ARM Linux
2018-09-14 14:38     ` Russell King - ARM Linux
2018-09-14 14:38     ` Russell King - ARM Linux
2018-09-12 15:48 ` [PATCHv2 2/3] PCI: mvebu: Convert to PCI emulated bridge config space Thomas Petazzoni
2018-09-12 15:48   ` Thomas Petazzoni
2018-09-14 14:44   ` Russell King - ARM Linux
2018-09-14 14:44     ` Russell King - ARM Linux
2018-09-18 16:17   ` Lorenzo Pieralisi
2018-09-18 16:17     ` Lorenzo Pieralisi
2018-09-18 16:17     ` Lorenzo Pieralisi
2018-10-04 10:48     ` Lorenzo Pieralisi
2018-10-04 10:48       ` Lorenzo Pieralisi
2018-10-04 11:13       ` Thomas Petazzoni
2018-10-04 11:13         ` Thomas Petazzoni
2018-10-04 11:26         ` Russell King - ARM Linux
2018-10-04 11:26           ` Russell King - ARM Linux
2018-09-12 15:48 ` [PATCHv2 3/3] PCI: aardvark: Implement emulated root PCI " Thomas Petazzoni
2018-09-12 15:48   ` Thomas Petazzoni

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.