If the upstream bridge has a static enumeration profile, then reassign all of the device's MMIO BARs to fit within the upstream bridge's memory space. Use a first-fit algorithm to minimize memory holes. Signed-off-by: Jason Tang <jason.tang2@xxxxxxx> --- drivers/pci/pci_static_enum.c | 175 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/drivers/pci/pci_static_enum.c b/drivers/pci/pci_static_enum.c index f000c7f..50f4db4 100644 --- a/drivers/pci/pci_static_enum.c +++ b/drivers/pci/pci_static_enum.c @@ -21,6 +21,12 @@ static DEFINE_SPINLOCK(static_enum_lock); +struct pci_static_enum_memspace { + struct list_head list; + resource_size_t start; + resource_size_t size; +}; + struct pci_static_enum_setting { struct list_head list; int seg; @@ -29,6 +35,7 @@ struct pci_static_enum_setting { unsigned char secondary_bus; unsigned char subordinate_bus; resource_size_t bridgemem_start, bridgemem_end; + struct list_head memspace; }; LIST_HEAD(settings_list); @@ -124,6 +131,7 @@ static int pci_static_enum_parse_options(void) setting->seg = seg; setting->bus = bus; setting->devfn = PCI_DEVFN(slot, func); + INIT_LIST_HEAD(&setting->memspace); list_add(&setting->list, &settings_list); } @@ -138,9 +146,18 @@ static int pci_static_enum_parse_options(void) } else if (sscanf(p, "M%llx-%llx%n", &start, &end, &count) == 2 && count > 0) { + struct pci_static_enum_memspace *mem; p += count; setting->bridgemem_start = start; setting->bridgemem_end = end; + mem = kmalloc(sizeof(*mem), GFP_KERNEL); + if (!mem) { + pr_err(PREFIX "Out of memory\n"); + return -1; + } + mem->start = start; + mem->size = end - start + 1; + list_add_tail(&mem->list, &setting->memspace); } else { pr_err(PREFIX "Invalid enum option: `%s'\n", p); return -1; @@ -358,6 +375,149 @@ static void pci_static_enum_set_bridge_memory(struct pci_dev *dev, struct pci_st } /** + * pci_static_enum_assign_space() - search for aligned memory + * @setting: device's static enumeration profile + * @requested_size: number of bytes requested by a BAR + * + * Use a first-fit algorithm to find a memory that is properly + * aligned. Reserve that memory for a BAR, and then update the free + * space list. + * + * Return: starting 32-bit address for memory, or 0 if insufficient + * space. + */ +static u32 pci_static_enum_assign_space(struct pci_static_enum_setting *setting, + u32 requested_size) +{ + struct pci_static_enum_memspace *mem; + int found_space = 0; + resource_size_t start_addr; + + list_for_each_entry(mem, &setting->memspace, list) { + /* make sure that free space is aligned */ + start_addr = ALIGN(mem->start, requested_size); + if (start_addr + requested_size < mem->start + mem->size) { + found_space = 1; + break; + } + } + + if (!found_space) + return 0; + + /* update the free space list */ + if (start_addr == mem->start && requested_size == mem->size) { + /* delete block */ + list_del(&mem->list); + kfree(mem); + } else if (start_addr == mem->start) { + /* trim from start */ + mem->start += requested_size; + mem->size -= requested_size; + } else if (start_addr + requested_size == mem->start + mem->size) { + /* trim from end */ + mem->size -= requested_size; + } else { + /* split block */ + struct pci_static_enum_memspace *mem2; + + mem2 = kmalloc(sizeof(*mem), GFP_KERNEL); + mem2->start = start_addr + requested_size; + mem2->size = start_addr + mem->size - mem2->start; + mem->size = mem2->start - mem->start; + list_add(&mem2->list, &mem->list); + } + return (u32) start_addr; +} + +#define PCI_COMMAND_DECODE_ENABLE (PCI_COMMAND_MEMORY | PCI_COMMAND_IO) + +/** + * pci_static_enum_reset_mmio_bar() - scan a particular BAR on a PCI + * device, reassigning the BAR if it is for a MMIO memory space + * @dev: PCI device to override + * @setting: static enumeration profile of the device's upstream bus + * @reg: offset into dev's configuration space, from which to read BAR + * value + */ +static void pci_static_enum_reset_mmio_bar(struct pci_dev *dev, struct pci_static_enum_setting + *setting, unsigned int reg) +{ + u32 orig_bar, sz; + u32 requested_size = 0; + u32 assigned_addr = 0; + u16 orig_cmd; + + if (!dev->mmio_always_on) { + pci_read_config_word(dev, PCI_COMMAND, &orig_cmd); + if (orig_cmd & PCI_COMMAND_DECODE_ENABLE) + pci_write_config_word(dev, PCI_COMMAND, + orig_cmd & + ~PCI_COMMAND_DECODE_ENABLE); + } + + /* fetch amount of address space needed by this PCI device */ + pci_read_config_dword(dev, reg, &orig_bar); + pci_write_config_dword(dev, reg, ~0); + pci_read_config_dword(dev, reg, &sz); + pci_write_config_dword(dev, reg, orig_bar); + + /* if no size is set, or size is invalid, or this BAR is for + I/O, then leave this BAR untouched */ + if (!sz || sz == 0xffffffff || (sz & PCI_BASE_ADDRESS_SPACE_IO)) + goto out; + + /* ignore prefetch memory and 64-bit BARs, for now */ + if ((sz & PCI_BASE_ADDRESS_MEM_PREFETCH)) + goto out; + + if ((sz & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == + PCI_BASE_ADDRESS_MEM_TYPE_64) + goto out; + + requested_size = ~(((u32) PCI_BASE_ADDRESS_MEM_MASK) & sz) + 1; + assigned_addr = pci_static_enum_assign_space(setting, requested_size); + if (assigned_addr > 0) + pci_write_config_dword(dev, reg, assigned_addr); + +out: + if (!dev->mmio_always_on && (orig_cmd & PCI_COMMAND_DECODE_ENABLE)) + pci_write_config_word(dev, PCI_COMMAND, orig_cmd); +} + +/** + * pci_static_enum_set_mmio_bars() - scan each BAR for this device, + * reassigning MMIO memory spaces + * @dev: PCI device to override + * @setting: static enumeration profile of the device's upstream bus + */ +static void pci_static_enum_set_mmio_bars(struct pci_dev *dev, struct pci_static_enum_setting + *setting) +{ + unsigned int howmany, pos, reg; + + switch (dev->hdr_type) { + case PCI_HEADER_TYPE_NORMAL: + howmany = 6; + break; + case PCI_HEADER_TYPE_BRIDGE: + howmany = 2; + break; + default: + howmany = 1; + break; + } + + /* for MMIO bars, find first allocation that fits within + upstream's bus */ + dev_dbg(&dev->dev, "Reassigning MMIO bars\n"); + for (pos = 0; pos < howmany; pos++) { + reg = PCI_BASE_ADDRESS_0 + (pos << 2); + pci_static_enum_reset_mmio_bar(dev, setting, reg); + } +} + +/** * pci_static_enum_device() - override a device's memory * configuration, according to its static enumeration profile * @dev: PCI device to override @@ -365,6 +525,7 @@ static void pci_static_enum_set_bridge_memory(struct pci_dev *dev, struct pci_st void pci_static_enum_device(struct pci_dev *dev) { struct pci_static_enum_setting *setting; + struct pci_dev *parent; /* if dev is a bridge, set its memory regions */ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { @@ -374,4 +535,18 @@ void pci_static_enum_device(struct pci_dev *dev) if (setting && setting->bridgemem_start) pci_static_enum_set_bridge_memory(dev, setting); } + + /* + * if dev's parent bus has a static enumeration profile, then + * reassign BARS to fit within the parent's memory regions + */ + parent = pci_upstream_bridge(dev); + + if (parent) { + setting = pci_static_enum_find_setting(pci_domain_nr(dev->bus), + parent->bus->number, + parent->devfn); + if (setting) + pci_static_enum_set_mmio_bars(dev, setting); + } } -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html