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

Commit 104d1e40 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/resource' into next

* pci/resource:
  PCI: Fail pci_map_rom() if the option ROM is invalid
  PCI: Move pci_map_rom() error path
  x86/PCI: Enable a 64bit BAR on AMD Family 15h (Models 00-1f, 30-3f, 60-7f)
  PCI: Add pci_resize_resource() for resizing BARs
  PCI: Add resizable BAR infrastructure
  PCI: Add PCI resource type mask #define
parents 8dceeaf8 a405f191
Loading
Loading
Loading
Loading
+85 −0
Original line number Original line Diff line number Diff line
@@ -635,3 +635,88 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2030, quirk_no_aersid);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2031, quirk_no_aersid);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2031, quirk_no_aersid);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2032, quirk_no_aersid);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2032, quirk_no_aersid);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2033, quirk_no_aersid);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2033, quirk_no_aersid);

#ifdef CONFIG_PHYS_ADDR_T_64BIT

#define AMD_141b_MMIO_BASE(x)	(0x80 + (x) * 0x8)
#define AMD_141b_MMIO_BASE_RE_MASK		BIT(0)
#define AMD_141b_MMIO_BASE_WE_MASK		BIT(1)
#define AMD_141b_MMIO_BASE_MMIOBASE_MASK	GENMASK(31,8)

#define AMD_141b_MMIO_LIMIT(x)	(0x84 + (x) * 0x8)
#define AMD_141b_MMIO_LIMIT_MMIOLIMIT_MASK	GENMASK(31,8)

#define AMD_141b_MMIO_HIGH(x)	(0x180 + (x) * 0x4)
#define AMD_141b_MMIO_HIGH_MMIOBASE_MASK	GENMASK(7,0)
#define AMD_141b_MMIO_HIGH_MMIOLIMIT_SHIFT	16
#define AMD_141b_MMIO_HIGH_MMIOLIMIT_MASK	GENMASK(23,16)

/*
 * The PCI Firmware Spec, rev 3.2, notes that ACPI should optionally allow
 * configuring host bridge windows using the _PRS and _SRS methods.
 *
 * But this is rarely implemented, so we manually enable a large 64bit BAR for
 * PCIe device on AMD Family 15h (Models 00h-1fh, 30h-3fh, 60h-7fh) Processors
 * here.
 */
static void pci_amd_enable_64bit_bar(struct pci_dev *dev)
{
	unsigned i;
	u32 base, limit, high;
	struct resource *res, *conflict;

	for (i = 0; i < 8; i++) {
		pci_read_config_dword(dev, AMD_141b_MMIO_BASE(i), &base);
		pci_read_config_dword(dev, AMD_141b_MMIO_HIGH(i), &high);

		/* Is this slot free? */
		if (!(base & (AMD_141b_MMIO_BASE_RE_MASK |
			      AMD_141b_MMIO_BASE_WE_MASK)))
			break;

		base >>= 8;
		base |= high << 24;

		/* Abort if a slot already configures a 64bit BAR. */
		if (base > 0x10000)
			return;
	}
	if (i == 8)
		return;

	res = kzalloc(sizeof(*res), GFP_KERNEL);
	if (!res)
		return;

	res->name = "PCI Bus 0000:00";
	res->flags = IORESOURCE_PREFETCH | IORESOURCE_MEM |
		IORESOURCE_MEM_64 | IORESOURCE_WINDOW;
	res->start = 0x100000000ull;
	res->end = 0xfd00000000ull - 1;

	/* Just grab the free area behind system memory for this */
	while ((conflict = request_resource_conflict(&iomem_resource, res)))
		res->start = conflict->end + 1;

	dev_info(&dev->dev, "adding root bus resource %pR\n", res);

	base = ((res->start >> 8) & AMD_141b_MMIO_BASE_MMIOBASE_MASK) |
		AMD_141b_MMIO_BASE_RE_MASK | AMD_141b_MMIO_BASE_WE_MASK;
	limit = ((res->end + 1) >> 8) & AMD_141b_MMIO_LIMIT_MMIOLIMIT_MASK;
	high = ((res->start >> 40) & AMD_141b_MMIO_HIGH_MMIOBASE_MASK) |
		((((res->end + 1) >> 40) << AMD_141b_MMIO_HIGH_MMIOLIMIT_SHIFT)
		 & AMD_141b_MMIO_HIGH_MMIOLIMIT_MASK);

	pci_write_config_dword(dev, AMD_141b_MMIO_HIGH(i), high);
	pci_write_config_dword(dev, AMD_141b_MMIO_LIMIT(i), limit);
	pci_write_config_dword(dev, AMD_141b_MMIO_BASE(i), base);

	pci_bus_add_resource(dev->bus, res, 0);
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x1401, pci_amd_enable_64bit_bar);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x141b, pci_amd_enable_64bit_bar);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x1571, pci_amd_enable_64bit_bar);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x15b1, pci_amd_enable_64bit_bar);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x1601, pci_amd_enable_64bit_bar);

#endif
+101 −0
Original line number Original line Diff line number Diff line
@@ -2965,6 +2965,107 @@ bool pci_acs_path_enabled(struct pci_dev *start,
	return true;
	return true;
}
}


/**
 * pci_rebar_find_pos - find position of resize ctrl reg for BAR
 * @pdev: PCI device
 * @bar: BAR to find
 *
 * Helper to find the position of the ctrl register for a BAR.
 * Returns -ENOTSUPP if resizable BARs are not supported at all.
 * Returns -ENOENT if no ctrl register for the BAR could be found.
 */
static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
{
	unsigned int pos, nbars, i;
	u32 ctrl;

	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
	if (!pos)
		return -ENOTSUPP;

	pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
	nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >>
		    PCI_REBAR_CTRL_NBAR_SHIFT;

	for (i = 0; i < nbars; i++, pos += 8) {
		int bar_idx;

		pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
		bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
		if (bar_idx == bar)
			return pos;
	}

	return -ENOENT;
}

/**
 * pci_rebar_get_possible_sizes - get possible sizes for BAR
 * @pdev: PCI device
 * @bar: BAR to query
 *
 * Get the possible sizes of a resizable BAR as bitmask defined in the spec
 * (bit 0=1MB, bit 19=512GB). Returns 0 if BAR isn't resizable.
 */
u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
{
	int pos;
	u32 cap;

	pos = pci_rebar_find_pos(pdev, bar);
	if (pos < 0)
		return 0;

	pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
	return (cap & PCI_REBAR_CAP_SIZES) >> 4;
}

/**
 * pci_rebar_get_current_size - get the current size of a BAR
 * @pdev: PCI device
 * @bar: BAR to set size to
 *
 * Read the size of a BAR from the resizable BAR config.
 * Returns size if found or negative error code.
 */
int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
{
	int pos;
	u32 ctrl;

	pos = pci_rebar_find_pos(pdev, bar);
	if (pos < 0)
		return pos;

	pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
	return (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> 8;
}

/**
 * pci_rebar_set_size - set a new size for a BAR
 * @pdev: PCI device
 * @bar: BAR to set size to
 * @size: new size as defined in the spec (0=1MB, 19=512GB)
 *
 * Set the new size of a BAR as defined in the spec.
 * Returns zero if resizing was successful, error code otherwise.
 */
int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
{
	int pos;
	u32 ctrl;

	pos = pci_rebar_find_pos(pdev, bar);
	if (pos < 0)
		return pos;

	pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
	ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
	ctrl |= size << 8;
	pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
	return 0;
}

/**
/**
 * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge
 * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge
 * @dev: the PCI device
 * @dev: the PCI device
+8 −0
Original line number Original line Diff line number Diff line
@@ -366,4 +366,12 @@ int acpi_get_rc_resources(struct device *dev, const char *hid, u16 segment,
			  struct resource *res);
			  struct resource *res);
#endif
#endif


u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar);
int pci_rebar_get_current_size(struct pci_dev *pdev, int bar);
int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size);
static inline u64 pci_rebar_size_to_bytes(int size)
{
	return 1ULL << (size + 20);
}

#endif /* DRIVERS_PCI_H */
#endif /* DRIVERS_PCI_H */
+13 −6
Original line number Original line Diff line number Diff line
@@ -147,12 +147,8 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
		return NULL;
		return NULL;


	rom = ioremap(start, *size);
	rom = ioremap(start, *size);
	if (!rom) {
	if (!rom)
		/* restore enable if ioremap fails */
		goto err_ioremap;
		if (!(res->flags & IORESOURCE_ROM_ENABLE))
			pci_disable_rom(pdev);
		return NULL;
	}


	/*
	/*
	 * Try to find the true size of the ROM since sometimes the PCI window
	 * Try to find the true size of the ROM since sometimes the PCI window
@@ -160,7 +156,18 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
	 * True size is important if the ROM is going to be copied.
	 * True size is important if the ROM is going to be copied.
	 */
	 */
	*size = pci_get_rom_size(pdev, rom, *size);
	*size = pci_get_rom_size(pdev, rom, *size);
	if (!*size)
		goto invalid_rom;

	return rom;
	return rom;

invalid_rom:
	iounmap(rom);
err_ioremap:
	/* restore enable if ioremap fails */
	if (!(res->flags & IORESOURCE_ROM_ENABLE))
		pci_disable_rom(pdev);
	return NULL;
}
}
EXPORT_SYMBOL(pci_map_rom);
EXPORT_SYMBOL(pci_map_rom);


+106 −9
Original line number Original line Diff line number Diff line
@@ -1518,13 +1518,16 @@ static void __pci_bridge_assign_resources(const struct pci_dev *bridge,
		break;
		break;
	}
	}
}
}

#define PCI_RES_TYPE_MASK \
	(IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH |\
	 IORESOURCE_MEM_64)

static void pci_bridge_release_resources(struct pci_bus *bus,
static void pci_bridge_release_resources(struct pci_bus *bus,
					  unsigned long type)
					  unsigned long type)
{
{
	struct pci_dev *dev = bus->self;
	struct pci_dev *dev = bus->self;
	struct resource *r;
	struct resource *r;
	unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
				  IORESOURCE_PREFETCH | IORESOURCE_MEM_64;
	unsigned old_flags = 0;
	unsigned old_flags = 0;
	struct resource *b_res;
	struct resource *b_res;
	int idx = 1;
	int idx = 1;
@@ -1567,7 +1570,7 @@ static void pci_bridge_release_resources(struct pci_bus *bus,
	 */
	 */
	release_child_resources(r);
	release_child_resources(r);
	if (!release_resource(r)) {
	if (!release_resource(r)) {
		type = old_flags = r->flags & type_mask;
		type = old_flags = r->flags & PCI_RES_TYPE_MASK;
		dev_printk(KERN_DEBUG, &dev->dev, "resource %d %pR released\n",
		dev_printk(KERN_DEBUG, &dev->dev, "resource %d %pR released\n",
					PCI_BRIDGE_RESOURCES + idx, r);
					PCI_BRIDGE_RESOURCES + idx, r);
		/* keep the old size */
		/* keep the old size */
@@ -1758,8 +1761,6 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus)
	enum release_type rel_type = leaf_only;
	enum release_type rel_type = leaf_only;
	LIST_HEAD(fail_head);
	LIST_HEAD(fail_head);
	struct pci_dev_resource *fail_res;
	struct pci_dev_resource *fail_res;
	unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
				  IORESOURCE_PREFETCH | IORESOURCE_MEM_64;
	int pci_try_num = 1;
	int pci_try_num = 1;
	enum enable_type enable_local;
	enum enable_type enable_local;


@@ -1818,7 +1819,7 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus)
	 */
	 */
	list_for_each_entry(fail_res, &fail_head, list)
	list_for_each_entry(fail_res, &fail_head, list)
		pci_bus_release_bridge_resources(fail_res->dev->bus,
		pci_bus_release_bridge_resources(fail_res->dev->bus,
						 fail_res->flags & type_mask,
						 fail_res->flags & PCI_RES_TYPE_MASK,
						 rel_type);
						 rel_type);


	/* restore size and flags */
	/* restore size and flags */
@@ -2031,8 +2032,6 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
	LIST_HEAD(fail_head);
	LIST_HEAD(fail_head);
	struct pci_dev_resource *fail_res;
	struct pci_dev_resource *fail_res;
	int retval;
	int retval;
	unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
				  IORESOURCE_PREFETCH | IORESOURCE_MEM_64;


again:
again:
	__pci_bus_size_bridges(parent, &add_list);
	__pci_bus_size_bridges(parent, &add_list);
@@ -2066,7 +2065,7 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
	 */
	 */
	list_for_each_entry(fail_res, &fail_head, list)
	list_for_each_entry(fail_res, &fail_head, list)
		pci_bus_release_bridge_resources(fail_res->dev->bus,
		pci_bus_release_bridge_resources(fail_res->dev->bus,
						 fail_res->flags & type_mask,
						 fail_res->flags & PCI_RES_TYPE_MASK,
						 whole_subtree);
						 whole_subtree);


	/* restore size and flags */
	/* restore size and flags */
@@ -2091,6 +2090,104 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
}
}
EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);


int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type)
{
	struct pci_dev_resource *dev_res;
	struct pci_dev *next;
	LIST_HEAD(saved);
	LIST_HEAD(added);
	LIST_HEAD(failed);
	unsigned int i;
	int ret;

	/* Walk to the root hub, releasing bridge BARs when possible */
	next = bridge;
	do {
		bridge = next;
		for (i = PCI_BRIDGE_RESOURCES; i < PCI_BRIDGE_RESOURCE_END;
		     i++) {
			struct resource *res = &bridge->resource[i];

			if ((res->flags ^ type) & PCI_RES_TYPE_MASK)
				continue;

			/* Ignore BARs which are still in use */
			if (res->child)
				continue;

			ret = add_to_list(&saved, bridge, res, 0, 0);
			if (ret)
				goto cleanup;

			dev_info(&bridge->dev, "BAR %d: releasing %pR\n",
				 i, res);

			if (res->parent)
				release_resource(res);
			res->start = 0;
			res->end = 0;
			break;
		}
		if (i == PCI_BRIDGE_RESOURCE_END)
			break;

		next = bridge->bus ? bridge->bus->self : NULL;
	} while (next);

	if (list_empty(&saved))
		return -ENOENT;

	__pci_bus_size_bridges(bridge->subordinate, &added);
	__pci_bridge_assign_resources(bridge, &added, &failed);
	BUG_ON(!list_empty(&added));

	if (!list_empty(&failed)) {
		ret = -ENOSPC;
		goto cleanup;
	}

	list_for_each_entry(dev_res, &saved, list) {
		/* Skip the bridge we just assigned resources for. */
		if (bridge == dev_res->dev)
			continue;

		bridge = dev_res->dev;
		pci_setup_bridge(bridge->subordinate);
	}

	free_list(&saved);
	return 0;

cleanup:
	/* restore size and flags */
	list_for_each_entry(dev_res, &failed, list) {
		struct resource *res = dev_res->res;

		res->start = dev_res->start;
		res->end = dev_res->end;
		res->flags = dev_res->flags;
	}
	free_list(&failed);

	/* Revert to the old configuration */
	list_for_each_entry(dev_res, &saved, list) {
		struct resource *res = dev_res->res;

		bridge = dev_res->dev;
		i = res - bridge->resource;

		res->start = dev_res->start;
		res->end = dev_res->end;
		res->flags = dev_res->flags;

		pci_claim_resource(bridge, i);
		pci_setup_bridge(bridge->subordinate);
	}
	free_list(&saved);

	return ret;
}

void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
{
{
	struct pci_dev *dev;
	struct pci_dev *dev;
Loading