Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3ebb9566 authored by Joerg Roedel's avatar Joerg Roedel
Browse files

Merge remote-tracking branch 'pci-bjorn/topic/alex-vfio-prep' into groups

parents 485802a6 a0dee2ed
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -162,7 +162,8 @@ int pci_user_read_config_##size \
	if (ret > 0)							\
		ret = -EINVAL;						\
	return ret;							\
}
}									\
EXPORT_SYMBOL_GPL(pci_user_read_config_##size);

/* Returns 0 on success, negative values indicate error. */
#define PCI_USER_WRITE_CONFIG(size,type)				\
@@ -181,7 +182,8 @@ int pci_user_write_config_##size \
	if (ret > 0)							\
		ret = -EINVAL;						\
	return ret;							\
}
}									\
EXPORT_SYMBOL_GPL(pci_user_write_config_##size);

PCI_USER_READ_CONFIG(byte, u8)
PCI_USER_READ_CONFIG(word, u16)
+69 −0
Original line number Diff line number Diff line
@@ -2364,6 +2364,75 @@ void pci_enable_acs(struct pci_dev *dev)
	pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
}

/**
 * pci_acs_enabled - test ACS against required flags for a given device
 * @pdev: device to test
 * @acs_flags: required PCI ACS flags
 *
 * Return true if the device supports the provided flags.  Automatically
 * filters out flags that are not implemented on multifunction devices.
 */
bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags)
{
	int pos, ret;
	u16 ctrl;

	ret = pci_dev_specific_acs_enabled(pdev, acs_flags);
	if (ret >= 0)
		return ret > 0;

	if (!pci_is_pcie(pdev))
		return false;

	/* Filter out flags not applicable to multifunction */
	if (pdev->multifunction)
		acs_flags &= (PCI_ACS_RR | PCI_ACS_CR |
			      PCI_ACS_EC | PCI_ACS_DT);

	if (pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM ||
	    pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT ||
	    pdev->multifunction) {
		pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS);
		if (!pos)
			return false;

		pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl);
		if ((ctrl & acs_flags) != acs_flags)
			return false;
	}

	return true;
}

/**
 * pci_acs_path_enable - test ACS flags from start to end in a hierarchy
 * @start: starting downstream device
 * @end: ending upstream device or NULL to search to the root bus
 * @acs_flags: required flags
 *
 * Walk up a device tree from start to end testing PCI ACS support.  If
 * any step along the way does not support the required flags, return false.
 */
bool pci_acs_path_enabled(struct pci_dev *start,
			  struct pci_dev *end, u16 acs_flags)
{
	struct pci_dev *pdev, *parent = start;

	do {
		pdev = parent;

		if (!pci_acs_enabled(pdev, acs_flags))
			return false;

		if (pci_is_root_bus(pdev->bus))
			return (end == NULL);

		parent = pdev->bus->self;
	} while (pdev != end);

	return true;
}

/**
 * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge
 * @dev: the PCI device
+0 −7
Original line number Diff line number Diff line
@@ -86,13 +86,6 @@ static inline bool pci_is_bridge(struct pci_dev *pci_dev)
	return !!(pci_dev->subordinate);
}

extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val);
extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val);
extern int pci_user_read_config_dword(struct pci_dev *dev, int where, u32 *val);
extern int pci_user_write_config_byte(struct pci_dev *dev, int where, u8 val);
extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val);
extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val);

struct pci_vpd_ops {
	ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
	ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf);
+84 −0
Original line number Diff line number Diff line
@@ -3205,3 +3205,87 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe)

	return -ENOTTY;
}

static struct pci_dev *pci_func_0_dma_source(struct pci_dev *dev)
{
	if (!PCI_FUNC(dev->devfn))
		return pci_dev_get(dev);

	return pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
}

static const struct pci_dev_dma_source {
	u16 vendor;
	u16 device;
	struct pci_dev *(*dma_source)(struct pci_dev *dev);
} pci_dev_dma_source[] = {
	/*
	 * https://bugzilla.redhat.com/show_bug.cgi?id=605888
	 *
	 * Some Ricoh devices use the function 0 source ID for DMA on
	 * other functions of a multifunction device.  The DMA devices
	 * is therefore function 0, which will have implications of the
	 * iommu grouping of these devices.
	 */
	{ PCI_VENDOR_ID_RICOH, 0xe822, pci_func_0_dma_source },
	{ PCI_VENDOR_ID_RICOH, 0xe230, pci_func_0_dma_source },
	{ PCI_VENDOR_ID_RICOH, 0xe832, pci_func_0_dma_source },
	{ PCI_VENDOR_ID_RICOH, 0xe476, pci_func_0_dma_source },
	{ 0 }
};

/*
 * IOMMUs with isolation capabilities need to be programmed with the
 * correct source ID of a device.  In most cases, the source ID matches
 * the device doing the DMA, but sometimes hardware is broken and will
 * tag the DMA as being sourced from a different device.  This function
 * allows that translation.  Note that the reference count of the
 * returned device is incremented on all paths.
 */
struct pci_dev *pci_get_dma_source(struct pci_dev *dev)
{
	const struct pci_dev_dma_source *i;

	for (i = pci_dev_dma_source; i->dma_source; i++) {
		if ((i->vendor == dev->vendor ||
		     i->vendor == (u16)PCI_ANY_ID) &&
		    (i->device == dev->device ||
		     i->device == (u16)PCI_ANY_ID))
			return i->dma_source(dev);
	}

	return pci_dev_get(dev);
}

static const struct pci_dev_acs_enabled {
	u16 vendor;
	u16 device;
	int (*acs_enabled)(struct pci_dev *dev, u16 acs_flags);
} pci_dev_acs_enabled[] = {
	{ 0 }
};

int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags)
{
	const struct pci_dev_acs_enabled *i;
	int ret;

	/*
	 * Allow devices that do not expose standard PCIe ACS capabilities
	 * or control to indicate their support here.  Multi-function express
	 * devices which do not allow internal peer-to-peer between functions,
	 * but do not implement PCIe ACS may wish to return true here.
	 */
	for (i = pci_dev_acs_enabled; i->acs_enabled; i++) {
		if ((i->vendor == dev->vendor ||
		     i->vendor == (u16)PCI_ANY_ID) &&
		    (i->device == dev->device ||
		     i->device == (u16)PCI_ANY_ID)) {
			ret = i->acs_enabled(dev, acs_flags);
			if (ret >= 0)
				return ret;
		}
	}

	return -ENOTTY;
}
+3 −3
Original line number Diff line number Diff line
@@ -124,7 +124,7 @@ static inline u32 merge_value(u32 val, u32 new_val, u32 new_val_mask,
	return val;
}

static int pcibios_err_to_errno(int err)
static int xen_pcibios_err_to_errno(int err)
{
	switch (err) {
	case PCIBIOS_SUCCESSFUL:
@@ -202,7 +202,7 @@ int xen_pcibk_config_read(struct pci_dev *dev, int offset, int size,
		       pci_name(dev), size, offset, value);

	*ret_val = value;
	return pcibios_err_to_errno(err);
	return xen_pcibios_err_to_errno(err);
}

int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value)
@@ -290,7 +290,7 @@ int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value)
		}
	}

	return pcibios_err_to_errno(err);
	return xen_pcibios_err_to_errno(err);
}

void xen_pcibk_config_free_dyn_fields(struct pci_dev *dev)
Loading