Commit c0bd4197 authored by Russell King's avatar Russell King Committed by Lorenzo Pieralisi
Browse files

PCI: pci-bridge-emul: Add support for PCIe extended capabilities

Add support for PCIe extended capabilities, which we just redirect to the
emulating driver.

[pali: Fix writing new value with W1C bits]
Link: https://lore.kernel.org/r/20220222155030.988-3-pali@kernel.org


Signed-off-by: default avatarRussell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: default avatarPali Rohár <pali@kernel.org>
Signed-off-by: default avatarMarek Behún <kabel@kernel.org>
Signed-off-by: default avatarLorenzo Pieralisi <lorenzo.pieralisi@arm.com>
parent c453bf6f
Loading
Loading
Loading
Loading
+52 −25
Original line number Diff line number Diff line
@@ -437,10 +437,16 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
		read_op = bridge->ops->read_pcie;
		cfgspace = (__le32 *) &bridge->pcie_conf;
		behavior = bridge->pcie_cap_regs_behavior;
	} else {
		/* Beyond our PCIe space */
	} else if (reg < PCI_CFG_SPACE_SIZE) {
		/* Rest of PCI space not implemented */
		*value = 0;
		return PCIBIOS_SUCCESSFUL;
	} else {
		/* PCIe extended capability space */
		reg -= PCI_CFG_SPACE_SIZE;
		read_op = bridge->ops->read_ext;
		cfgspace = NULL;
		behavior = NULL;
	}

	if (read_op)
@@ -448,13 +454,18 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where,
	else
		ret = PCI_BRIDGE_EMUL_NOT_HANDLED;

	if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED)
	if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED) {
		if (cfgspace)
			*value = le32_to_cpu(cfgspace[reg / 4]);
		else
			*value = 0;
	}

	/*
	 * Make sure we never return any reserved bit with a value
	 * different from 0.
	 */
	if (behavior)
		*value &= behavior[reg / 4].ro | behavior[reg / 4].rw |
			  behavior[reg / 4].w1c;

@@ -502,8 +513,15 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
		write_op = bridge->ops->write_pcie;
		cfgspace = (__le32 *) &bridge->pcie_conf;
		behavior = bridge->pcie_cap_regs_behavior;
	} else {
	} else if (reg < PCI_CFG_SPACE_SIZE) {
		/* Rest of PCI space not implemented */
		return PCIBIOS_SUCCESSFUL;
	} else {
		/* PCIe extended capability space */
		reg -= PCI_CFG_SPACE_SIZE;
		write_op = bridge->ops->write_ext;
		cfgspace = NULL;
		behavior = NULL;
	}

	shift = (where & 0x3) * 8;
@@ -517,6 +535,7 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
	else
		return PCIBIOS_BAD_REGISTER_NUMBER;

	if (behavior) {
		/* Keep all bits, except the RW bits */
		new = old & (~mask | ~behavior[reg / 4].rw);

@@ -525,10 +544,17 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,

		/* Clear the W1C bits */
		new &= ~((value << shift) & (behavior[reg / 4].w1c & mask));
	} else {
		new = old & ~mask;
		new |= (value << shift) & mask;
	}

	if (cfgspace) {
		/* Save the new value with the cleared W1C bits into the cfgspace */
		cfgspace[reg / 4] = cpu_to_le32(new);
	}

	if (behavior) {
		/*
		 * Clear the W1C bits not specified by the write mask, so that the
		 * write_op() does not clear them.
@@ -540,6 +566,7 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where,
		 * knows about that they are to be cleared.
		 */
		new |= (value << shift) & (behavior[reg / 4].w1c & mask);
	}

	if (write_op)
		write_op(bridge, reg, old, new, mask);
+15 −0
Original line number Diff line number Diff line
@@ -90,6 +90,14 @@ struct pci_bridge_emul_ops {
	 */
	pci_bridge_emul_read_status_t (*read_pcie)(struct pci_bridge_emul *bridge,
						   int reg, u32 *value);

	/*
	 * Same as ->read_base(), except it is for reading from the
	 * PCIe extended capability configuration space.
	 */
	pci_bridge_emul_read_status_t (*read_ext)(struct pci_bridge_emul *bridge,
						  int reg, u32 *value);

	/*
	 * Called when writing to the regular PCI bridge configuration
	 * space. old is the current value, new is the new value being
@@ -105,6 +113,13 @@ struct pci_bridge_emul_ops {
	 */
	void (*write_pcie)(struct pci_bridge_emul *bridge, int reg,
			   u32 old, u32 new, u32 mask);

	/*
	 * Same as ->write_base(), except it is for writing from the
	 * PCIe extended capability configuration space.
	 */
	void (*write_ext)(struct pci_bridge_emul *bridge, int reg,
			  u32 old, u32 new, u32 mask);
};

struct pci_bridge_reg_behavior;