diff options
| author | Bjorn Helgaas <bhelgaas@google.com> | 2026-02-06 17:09:25 -0600 |
|---|---|---|
| committer | Bjorn Helgaas <bhelgaas@google.com> | 2026-02-06 17:09:25 -0600 |
| commit | 73b4779864b1e6adad015d14047ae63b88ef9c4c (patch) | |
| tree | e54041fcb46fb738c15cf7c300de1584cbc60793 | |
| parent | bf37448d9b7793544904ccf21e5844b6ff4af3c0 (diff) | |
| parent | 2ecc1bf14e2fdaff78bd1b8e7ed3dba336a3fad5 (diff) | |
| download | linux-next-73b4779864b1e6adad015d14047ae63b88ef9c4c.tar.gz linux-next-73b4779864b1e6adad015d14047ae63b88ef9c4c.zip | |
Merge branch 'pci/resource'
- Build zero-sized resources when a BAR is larger than 4G but
pci_bus_addr_t or resource_size_t can't represent 64-bit addresses (Ilpo
Järvinen)
- Fix bridge window alignment with optional resources, where we previously
lost the additional alignment requirement (Ilpo Järvinen)
- Stop over-estimating bridge window size since we now assign them without
any gaps between them (Ilpo Järvinen)
- Increase resource MAX_IORES_LEVEL to avoid /proc/iomem flattening for
nested bridges and endpoints (Ilpo Järvinen)
- Remove old_size limit from bridge window sizing (Ilpo Järvinen)
- Push realloc check into pbus_size_mem() to simplify callers (Ilpo
Järvinen)
- Pass bridge window resource to pbus_size_mem() to avoid looking it up
again (Ilpo Järvinen)
- Use res_to_dev_res() instead of open-coding the same search (Ilpo
Järvinen)
- Add pci_resource_is_bridge_win() helper (Ilpo Järvinen)
- Add more logging of resource assignment (Ilpo Järvinen)
- Add pbus_mem_size_optional() to handle sizes of optional resources
(SR-IOV VF BARs, expansion ROMs, bridge windows) (Ilpo Järvinen)
- Move CardBus code to setup-cardbus.c and only build it when
CONFIG_CARDBUS is set (Ilpo Järvinen)
- Use scnprintf() instead of sprintf() (Ilpo Järvinen)
- Add pbus_validate_busn() for Bus Number validation (Ilpo Järvinen)
- Don't claim disabled bridge windows to avoid spurious claim failures
(Ilpo Järvinen)
* pci/resource:
PCI: Don't claim disabled bridge windows
PCI: Move CardBus bridge scanning to setup-cardbus.c
PCI: Add pbus_validate_busn() for Bus Number validation
PCI: Add dword #defines for Bus Number + Secondary Latency Timer
PCI: Use scnprintf() instead of sprintf()
PCI: Handle CardBus-specific params in setup-cardbus.c
PCI: Separate CardBus setup & build it only with CONFIG_CARDBUS
PCI: Add 'pci' prefix to struct pci_dev_resource handling functions
PCI: Use resource_assigned() in setup-bus.c algorithm
resource: Mark res given to resource_assigned() as const
PCI: Add pbus_mem_size_optional() to handle optional sizes
PCI: Check invalid align earlier in pbus_size_mem()
PCI: Log reset and restore of resources
PCI: Add pci_resource_is_bridge_win()
PCI: Fetch dev_res to local var in __assign_resources_sorted()
PCI: Use res_to_dev_res() in reassign_resources_sorted()
PCI: Pass bridge window resource to pbus_size_mem()
PCI: Push realloc check into pbus_size_mem()
PCI: Remove old_size limit from bridge window sizing
resource: Increase MAX_IORES_LEVEL to 8
PCI: Stop over-estimating bridge window size
PCI: Rewrite bridge window head alignment function
PCI: Fix bridge window alignment with optional resources
PCI: Use resource_set_range() that correctly sets ->end
| -rw-r--r-- | drivers/pci/Makefile | 1 | ||||
| -rw-r--r-- | drivers/pci/pci-sysfs.c | 2 | ||||
| -rw-r--r-- | drivers/pci/pci.c | 14 | ||||
| -rw-r--r-- | drivers/pci/pci.h | 49 | ||||
| -rw-r--r-- | drivers/pci/probe.c | 134 | ||||
| -rw-r--r-- | drivers/pci/setup-bus.c | 646 | ||||
| -rw-r--r-- | drivers/pci/setup-cardbus.c | 306 | ||||
| -rw-r--r-- | drivers/pci/setup-res.c | 2 | ||||
| -rw-r--r-- | drivers/pcmcia/yenta_socket.c | 2 | ||||
| -rw-r--r-- | include/linux/ioport.h | 2 | ||||
| -rw-r--r-- | include/linux/pci.h | 6 | ||||
| -rw-r--r-- | include/uapi/linux/pci_regs.h | 5 | ||||
| -rw-r--r-- | kernel/resource.c | 2 |
13 files changed, 652 insertions, 519 deletions
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index e10cfe5a280b..8922f90afecb 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_PCI_TSM) += tsm.o obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o obj-$(CONFIG_PCI_NPEM) += npem.o obj-$(CONFIG_PCIE_TPH) += tph.o +obj-$(CONFIG_CARDBUS) += setup-cardbus.o # Endpoint library must be initialized before its users obj-$(CONFIG_PCI_ENDPOINT) += endpoint/ diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index c2df915ad2d2..363187ba4f56 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -181,7 +181,7 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr, struct resource zerores = {}; /* For backwards compatibility */ - if (i >= PCI_BRIDGE_RESOURCES && i <= PCI_BRIDGE_RESOURCE_END && + if (pci_resource_is_bridge_win(i) && res->flags & (IORESOURCE_UNSET | IORESOURCE_DISABLED)) res = &zerores; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 835efe729a62..de7c8f6def12 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -99,12 +99,6 @@ bool pci_reset_supported(struct pci_dev *dev) int pci_domains_supported = 1; #endif -#define DEFAULT_CARDBUS_IO_SIZE (256) -#define DEFAULT_CARDBUS_MEM_SIZE (64*1024*1024) -/* pci=cbmemsize=nnM,cbiosize=nn can override this */ -unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE; -unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE; - #define DEFAULT_HOTPLUG_IO_SIZE (256) #define DEFAULT_HOTPLUG_MMIO_SIZE (2*1024*1024) #define DEFAULT_HOTPLUG_MMIO_PREF_SIZE (2*1024*1024) @@ -6639,7 +6633,9 @@ static int __init pci_setup(char *str) if (k) *k++ = 0; if (*str && (str = pcibios_setup(str)) && *str) { - if (!strcmp(str, "nomsi")) { + if (!pci_setup_cardbus(str)) { + /* Function handled the parameters */ + } else if (!strcmp(str, "nomsi")) { pci_no_msi(); } else if (!strncmp(str, "noats", 5)) { pr_info("PCIe: ATS is disabled\n"); @@ -6658,10 +6654,6 @@ static int __init pci_setup(char *str) pcie_ari_disabled = true; } else if (!strncmp(str, "notph", 5)) { pci_no_tph(); - } else if (!strncmp(str, "cbiosize=", 9)) { - pci_cardbus_io_size = memparse(str + 9, &str); - } else if (!strncmp(str, "cbmemsize=", 10)) { - pci_cardbus_mem_size = memparse(str + 10, &str); } else if (!strncmp(str, "resource_alignment=", 19)) { resource_alignment_param = str + 19; } else if (!strncmp(str, "ecrc=", 5)) { diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e3c2852c80fb..7930d9d3182e 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -245,6 +245,7 @@ void pci_config_pm_runtime_put(struct pci_dev *dev); void pci_pm_power_up_and_verify_state(struct pci_dev *pci_dev); void pci_pm_init(struct pci_dev *dev); void pci_ea_init(struct pci_dev *dev); +bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub); void pci_msi_init(struct pci_dev *dev); void pci_msix_init(struct pci_dev *dev); bool pci_bridge_d3_possible(struct pci_dev *dev); @@ -379,8 +380,40 @@ extern unsigned long pci_hotplug_io_size; extern unsigned long pci_hotplug_mmio_size; extern unsigned long pci_hotplug_mmio_pref_size; extern unsigned long pci_hotplug_bus_size; -extern unsigned long pci_cardbus_io_size; -extern unsigned long pci_cardbus_mem_size; + +static inline bool pci_is_cardbus_bridge(struct pci_dev *dev) +{ + return dev->hdr_type == PCI_HEADER_TYPE_CARDBUS; +} +#ifdef CONFIG_CARDBUS +unsigned long pci_cardbus_resource_alignment(struct resource *res); +int pci_bus_size_cardbus_bridge(struct pci_bus *bus, + struct list_head *realloc_head); +int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, + u32 buses, int max, + unsigned int available_buses, int pass); +int pci_setup_cardbus(char *str); + +#else +static inline unsigned long pci_cardbus_resource_alignment(struct resource *res) +{ + return 0; +} +static inline int pci_bus_size_cardbus_bridge(struct pci_bus *bus, + struct list_head *realloc_head) +{ + return -EOPNOTSUPP; +} +static inline int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, + struct pci_dev *dev, + u32 buses, int max, + unsigned int available_buses, + int pass) +{ + return max; +} +static inline int pci_setup_cardbus(char *str) { return -ENOENT; } +#endif /* CONFIG_CARDBUS */ /** * pci_match_one_device - Tell if a PCI device structure has a matching @@ -443,6 +476,10 @@ void __pci_size_stdbars(struct pci_dev *dev, int count, int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, struct resource *res, unsigned int reg, u32 *sizes); void pci_configure_ari(struct pci_dev *dev); + +int pci_dev_res_add_to_list(struct list_head *head, struct pci_dev *dev, + struct resource *res, resource_size_t add_size, + resource_size_t min_align); void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head); void __pci_bus_assign_resources(const struct pci_bus *bus, @@ -455,6 +492,11 @@ void pci_walk_bus_locked(struct pci_bus *top, const char *pci_resource_name(struct pci_dev *dev, unsigned int i); bool pci_resource_is_optional(const struct pci_dev *dev, int resno); +static inline bool pci_resource_is_bridge_win(int resno) +{ + return resno >= PCI_BRIDGE_RESOURCES && + resno <= PCI_BRIDGE_RESOURCE_END; +} /** * pci_resource_num - Reverse lookup resource number from device resources @@ -478,6 +520,7 @@ static inline int pci_resource_num(const struct pci_dev *dev, return resno; } +void pbus_validate_busn(struct pci_bus *bus); struct resource *pbus_select_window(struct pci_bus *bus, const struct resource *res); void pci_reassigndev_resource_alignment(struct pci_dev *dev); @@ -927,8 +970,6 @@ static inline void pci_suspend_ptm(struct pci_dev *dev) { } static inline void pci_resume_ptm(struct pci_dev *dev) { } #endif -unsigned long pci_cardbus_resource_alignment(struct resource *); - static inline resource_size_t pci_resource_alignment(struct pci_dev *dev, struct resource *res) { diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 3868b23e0f94..9494e7f6e36f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -14,6 +14,7 @@ #include <linux/platform_device.h> #include <linux/pci_hotplug.h> #include <linux/slab.h> +#include <linux/sprintf.h> #include <linux/module.h> #include <linux/cpumask.h> #include <linux/aer.h> @@ -24,9 +25,6 @@ #include <linux/bitfield.h> #include "pci.h" -#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ -#define CARDBUS_RESERVE_BUSNR 3 - static struct resource busn_resource = { .name = "PCI busn", .start = 0, @@ -287,8 +285,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, if ((sizeof(pci_bus_addr_t) < 8 || sizeof(resource_size_t) < 8) && sz64 > 0x100000000ULL) { res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED; - res->start = 0; - res->end = 0; + resource_set_range(res, 0, 0); pci_err(dev, "%s: can't handle BAR larger than 4GB (size %#010llx)\n", res_name, (unsigned long long)sz64); goto out; @@ -297,8 +294,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, if ((sizeof(pci_bus_addr_t) < 8) && l) { /* Above 32-bit boundary; try to reallocate */ res->flags |= IORESOURCE_UNSET; - res->start = 0; - res->end = sz64 - 1; + resource_set_range(res, 0, sz64); pci_info(dev, "%s: can't handle BAR above 4GB (bus address %#010llx)\n", res_name, (unsigned long long)l64); goto out; @@ -525,8 +521,8 @@ static void pci_read_bridge_windows(struct pci_dev *bridge) pci_read_config_dword(bridge, PCI_PRIMARY_BUS, &buses); res.flags = IORESOURCE_BUS; - res.start = (buses >> 8) & 0xff; - res.end = (buses >> 16) & 0xff; + res.start = FIELD_GET(PCI_SECONDARY_BUS_MASK, buses); + res.end = FIELD_GET(PCI_SUBORDINATE_BUS_MASK, buses); pci_info(bridge, "PCI bridge to %pR%s\n", &res, bridge->transparent ? " (subtractive decode)" : ""); @@ -1313,6 +1309,26 @@ static void pci_enable_rrs_sv(struct pci_dev *pdev) static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus, unsigned int available_buses); + +void pbus_validate_busn(struct pci_bus *bus) +{ + struct pci_bus *upstream = bus->parent; + struct pci_dev *bridge = bus->self; + + /* Check that all devices are accessible */ + while (upstream->parent) { + if ((bus->busn_res.end > upstream->busn_res.end) || + (bus->number > upstream->busn_res.end) || + (bus->number < upstream->number) || + (bus->busn_res.end < upstream->number)) { + pci_info(bridge, "devices behind bridge are unusable because %pR cannot be assigned for them\n", + &bus->busn_res); + break; + } + upstream = upstream->parent; + } +} + /** * pci_ea_fixed_busnrs() - Read fixed Secondary and Subordinate bus * numbers from EA capability. @@ -1324,7 +1340,7 @@ static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus, * and subordinate bus numbers, return true with the bus numbers in @sec * and @sub. Otherwise return false. */ -static bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub) +bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub) { int ea, offset; u32 dw; @@ -1378,8 +1394,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, int pass) { struct pci_bus *child; - int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS); - u32 buses, i, j = 0; + u32 buses; u16 bctl; u8 primary, secondary, subordinate; int broken = 0; @@ -1394,9 +1409,9 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, pm_runtime_get_sync(&dev->dev); pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses); - primary = buses & 0xFF; - secondary = (buses >> 8) & 0xFF; - subordinate = (buses >> 16) & 0xFF; + primary = FIELD_GET(PCI_PRIMARY_BUS_MASK, buses); + secondary = FIELD_GET(PCI_SECONDARY_BUS_MASK, buses); + subordinate = FIELD_GET(PCI_SUBORDINATE_BUS_MASK, buses); pci_dbg(dev, "scanning [bus %02x-%02x] behind bridge, pass %d\n", secondary, subordinate, pass); @@ -1423,8 +1438,15 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT); - if ((secondary || subordinate) && !pcibios_assign_all_busses() && - !is_cardbus && !broken) { + if (pci_is_cardbus_bridge(dev)) { + max = pci_cardbus_scan_bridge_extend(bus, dev, buses, max, + available_buses, + pass); + goto out; + } + + if ((secondary || subordinate) && + !pcibios_assign_all_busses() && !broken) { unsigned int cmax, buses; /* @@ -1466,7 +1488,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, * do in the second pass. */ if (!pass) { - if (pcibios_assign_all_busses() || broken || is_cardbus) + if (pcibios_assign_all_busses() || broken) /* * Temporarily disable forwarding of the @@ -1477,7 +1499,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, * ranges. */ pci_write_config_dword(dev, PCI_PRIMARY_BUS, - buses & ~0xffffff); + buses & PCI_SEC_LATENCY_TIMER_MASK); goto out; } @@ -1508,59 +1530,16 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, if (available_buses) available_buses--; - buses = (buses & 0xff000000) - | ((unsigned int)(child->primary) << 0) - | ((unsigned int)(child->busn_res.start) << 8) - | ((unsigned int)(child->busn_res.end) << 16); - - /* - * yenta.c forces a secondary latency timer of 176. - * Copy that behaviour here. - */ - if (is_cardbus) { - buses &= ~0xff000000; - buses |= CARDBUS_LATENCY_TIMER << 24; - } + buses = (buses & PCI_SEC_LATENCY_TIMER_MASK) | + FIELD_PREP(PCI_PRIMARY_BUS_MASK, child->primary) | + FIELD_PREP(PCI_SECONDARY_BUS_MASK, child->busn_res.start) | + FIELD_PREP(PCI_SUBORDINATE_BUS_MASK, child->busn_res.end); /* We need to blast all three values with a single write */ pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses); - if (!is_cardbus) { - child->bridge_ctl = bctl; - max = pci_scan_child_bus_extend(child, available_buses); - } else { - - /* - * For CardBus bridges, we leave 4 bus numbers as - * cards with a PCI-to-PCI bridge can be inserted - * later. - */ - for (i = 0; i < CARDBUS_RESERVE_BUSNR; i++) { - struct pci_bus *parent = bus; - if (pci_find_bus(pci_domain_nr(bus), - max+i+1)) - break; - while (parent->parent) { - if ((!pcibios_assign_all_busses()) && - (parent->busn_res.end > max) && - (parent->busn_res.end <= max+i)) { - j = 1; - } - parent = parent->parent; - } - if (j) { - - /* - * Often, there are two CardBus - * bridges -- try to leave one - * valid bus number for each one. - */ - i /= 2; - break; - } - } - max += i; - } + child->bridge_ctl = bctl; + max = pci_scan_child_bus_extend(child, available_buses); /* * Set subordinate bus number to its real value. @@ -1572,23 +1551,10 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, pci_bus_update_busn_res_end(child, max); pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); } + scnprintf(child->name, sizeof(child->name), "PCI Bus %04x:%02x", + pci_domain_nr(bus), child->number); - sprintf(child->name, - (is_cardbus ? "PCI CardBus %04x:%02x" : "PCI Bus %04x:%02x"), - pci_domain_nr(bus), child->number); - - /* Check that all devices are accessible */ - while (bus->parent) { - if ((child->busn_res.end > bus->busn_res.end) || - (child->number > bus->busn_res.end) || - (child->number < bus->number) || - (child->busn_res.end < bus->number)) { - dev_info(&dev->dev, "devices behind bridge are unusable because %pR cannot be assigned for them\n", - &child->busn_res); - break; - } - bus = bus->parent; - } + pbus_validate_busn(child); out: /* Clear errors in the Secondary Status Register */ diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 6e90f46f52af..69abb11b7e47 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -14,10 +14,12 @@ * tighter packing. Prefetchable range support. */ +#include <linux/align.h> #include <linux/bitops.h> #include <linux/bug.h> #include <linux/init.h> #include <linux/kernel.h> +#include <linux/minmax.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/errno.h> @@ -47,7 +49,7 @@ struct pci_dev_resource { unsigned long flags; }; -static void free_list(struct list_head *head) +static void pci_dev_res_free_list(struct list_head *head) { struct pci_dev_resource *dev_res, *tmp; @@ -58,16 +60,16 @@ static void free_list(struct list_head *head) } /** - * add_to_list() - Add a new resource tracker to the list + * pci_dev_res_add_to_list() - Add a new resource tracker to the list * @head: Head of the list * @dev: Device to which the resource belongs * @res: Resource to be tracked * @add_size: Additional size to be optionally added to the resource * @min_align: Minimum memory window alignment */ -static int add_to_list(struct list_head *head, struct pci_dev *dev, - struct resource *res, resource_size_t add_size, - resource_size_t min_align) +int pci_dev_res_add_to_list(struct list_head *head, struct pci_dev *dev, + struct resource *res, resource_size_t add_size, + resource_size_t min_align) { struct pci_dev_resource *tmp; @@ -88,7 +90,8 @@ static int add_to_list(struct list_head *head, struct pci_dev *dev, return 0; } -static void remove_from_list(struct list_head *head, struct resource *res) +static void pci_dev_res_remove_from_list(struct list_head *head, + struct resource *res) { struct pci_dev_resource *dev_res, *tmp; @@ -123,25 +126,21 @@ static resource_size_t get_res_add_size(struct list_head *head, return dev_res ? dev_res->add_size : 0; } -static resource_size_t get_res_add_align(struct list_head *head, - struct resource *res) -{ - struct pci_dev_resource *dev_res; - - dev_res = res_to_dev_res(head, res); - return dev_res ? dev_res->min_align : 0; -} - -static void restore_dev_resource(struct pci_dev_resource *dev_res) +static void pci_dev_res_restore(struct pci_dev_resource *dev_res) { struct resource *res = dev_res->res; + struct pci_dev *dev = dev_res->dev; + int idx = pci_resource_num(dev, res); + const char *res_name = pci_resource_name(dev, idx); - if (WARN_ON_ONCE(res->parent)) + if (WARN_ON_ONCE(resource_assigned(res))) return; res->start = dev_res->start; res->end = dev_res->end; res->flags = dev_res->flags; + + pci_dbg(dev, "%s %pR: resource restored\n", res_name, res); } /* @@ -168,7 +167,7 @@ static struct resource *find_bus_resource_of_type(struct pci_bus *bus, if ((r->flags & type_mask) != type) continue; - if (!r->parent) + if (!resource_assigned(r)) return r; if (!r_assigned) r_assigned = r; @@ -271,7 +270,7 @@ static struct resource *pbus_select_window_for_type(struct pci_bus *bus, struct resource *pbus_select_window(struct pci_bus *bus, const struct resource *res) { - if (res->parent) + if (resource_assigned(res)) return res->parent; return pbus_select_window_for_type(bus, res->flags); @@ -302,8 +301,7 @@ static bool pdev_resource_assignable(struct pci_dev *dev, struct resource *res) if (!res->flags) return false; - if (idx >= PCI_BRIDGE_RESOURCES && idx <= PCI_BRIDGE_RESOURCE_END && - res->flags & IORESOURCE_DISABLED) + if (pci_resource_is_bridge_win(idx) && res->flags & IORESOURCE_DISABLED) return false; return true; @@ -311,7 +309,7 @@ static bool pdev_resource_assignable(struct pci_dev *dev, struct resource *res) static bool pdev_resource_should_fit(struct pci_dev *dev, struct resource *res) { - if (res->parent) + if (resource_assigned(res)) return false; if (res->flags & IORESOURCE_PCI_FIXED) @@ -380,19 +378,24 @@ bool pci_resource_is_optional(const struct pci_dev *dev, int resno) return true; if (resno == PCI_ROM_RESOURCE && !(res->flags & IORESOURCE_ROM_ENABLE)) return true; + if (pci_resource_is_bridge_win(resno) && !resource_size(res)) + return true; return false; } -static inline void reset_resource(struct pci_dev *dev, struct resource *res) +static void reset_resource(struct pci_dev *dev, struct resource *res) { int idx = pci_resource_num(dev, res); + const char *res_name = pci_resource_name(dev, idx); - if (idx >= PCI_BRIDGE_RESOURCES && idx <= PCI_BRIDGE_RESOURCE_END) { + if (pci_resource_is_bridge_win(idx)) { res->flags |= IORESOURCE_UNSET; return; } + pci_dbg(dev, "%s %pR: resetting resource\n", res_name, res); + res->start = 0; res->end = 0; res->flags = 0; @@ -413,7 +416,6 @@ static void reassign_resources_sorted(struct list_head *realloc_head, struct list_head *head) { struct pci_dev_resource *add_res, *tmp; - struct pci_dev_resource *dev_res; struct pci_dev *dev; struct resource *res; const char *res_name; @@ -421,8 +423,6 @@ static void reassign_resources_sorted(struct list_head *realloc_head, int idx; list_for_each_entry_safe(add_res, tmp, realloc_head, list) { - bool found_match = false; - res = add_res->res; dev = add_res->dev; idx = pci_resource_num(dev, res); @@ -431,24 +431,18 @@ static void reassign_resources_sorted(struct list_head *realloc_head, * Skip resource that failed the earlier assignment and is * not optional as it would just fail again. */ - if (!res->parent && resource_size(res) && + if (!resource_assigned(res) && resource_size(res) && !pci_resource_is_optional(dev, idx)) goto out; /* Skip this resource if not found in head list */ - list_for_each_entry(dev_res, head, list) { - if (dev_res->res == res) { - found_match = true; - break; - } - } - if (!found_match) /* Just skip */ + if (!res_to_dev_res(head, res)) continue; res_name = pci_resource_name(dev, idx); add_size = add_res->add_size; align = add_res->min_align; - if (!res->parent) { + if (!resource_assigned(res)) { resource_set_range(res, align, resource_size(res) + add_size); if (pci_assign_resource(dev, idx)) { @@ -456,7 +450,7 @@ static void reassign_resources_sorted(struct list_head *realloc_head, "%s %pR: ignoring failure in optional allocation\n", res_name, res); } - } else if (add_size > 0) { + } else if (add_size > 0 || !IS_ALIGNED(res->start, align)) { res->flags |= add_res->flags & (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN); if (pci_reassign_resource(dev, idx, add_size, align)) @@ -505,9 +499,9 @@ static void assign_requested_resources_sorted(struct list_head *head, if (pci_assign_resource(dev, idx)) { if (fail_head) { - add_to_list(fail_head, dev, res, - 0 /* don't care */, - 0 /* don't care */); + pci_dev_res_add_to_list(fail_head, dev, res, + 0 /* don't care */, + 0 /* don't care */); } } } @@ -604,11 +598,11 @@ static void __assign_resources_sorted(struct list_head *head, LIST_HEAD(local_fail_head); LIST_HEAD(dummy_head); struct pci_dev_resource *save_res; - struct pci_dev_resource *dev_res, *tmp_res, *dev_res2; + struct pci_dev_resource *dev_res, *tmp_res, *dev_res2, *addsize_res; struct resource *res; struct pci_dev *dev; unsigned long fail_type; - resource_size_t add_align, align; + resource_size_t align; if (!realloc_head) realloc_head = &dummy_head; @@ -619,8 +613,9 @@ static void __assign_resources_sorted(struct list_head *head, /* Save original start, end, flags etc at first */ list_for_each_entry(dev_res, head, list) { - if (add_to_list(&save_head, dev_res->dev, dev_res->res, 0, 0)) { - free_list(&save_head); + if (pci_dev_res_add_to_list(&save_head, dev_res->dev, + dev_res->res, 0, 0)) { + pci_dev_res_free_list(&save_head); goto assign; } } @@ -629,8 +624,11 @@ static void __assign_resources_sorted(struct list_head *head, list_for_each_entry_safe(dev_res, tmp_res, head, list) { res = dev_res->res; - res->end += get_res_add_size(realloc_head, res); + addsize_res = res_to_dev_res(realloc_head, res); + if (!addsize_res) + continue; + res->end += addsize_res->add_size; /* * There are two kinds of additional resources in the list: * 1. bridge resource -- IORESOURCE_STARTALIGN @@ -640,8 +638,8 @@ static void __assign_resources_sorted(struct list_head *head, if (!(res->flags & IORESOURCE_STARTALIGN)) continue; - add_align = get_res_add_align(realloc_head, res); - + if (addsize_res->min_align <= res->start) + continue; /* * The "head" list is sorted by alignment so resources with * bigger alignment will be assigned first. After we @@ -649,17 +647,15 @@ static void __assign_resources_sorted(struct list_head *head, * need to reorder the list by alignment to make it * consistent. */ - if (add_align > res->start) { - resource_set_range(res, add_align, resource_size(res)); - - list_for_each_entry(dev_res2, head, list) { - align = pci_resource_alignment(dev_res2->dev, - dev_res2->res); - if (add_align > align) { - list_move_tail(&dev_res->list, - &dev_res2->list); - break; - } + resource_set_range(res, addsize_res->min_align, + resource_size(res)); + + list_for_each_entry(dev_res2, head, list) { + align = pci_resource_alignment(dev_res2->dev, + dev_res2->res); + if (addsize_res->min_align > align) { + list_move_tail(&dev_res->list, &dev_res2->list); + break; } } @@ -672,8 +668,9 @@ assign: if (list_empty(&local_fail_head)) { /* Remove head list from realloc_head list */ list_for_each_entry(dev_res, head, list) - remove_from_list(realloc_head, dev_res->res); - free_list(&save_head); + pci_dev_res_remove_from_list(realloc_head, + dev_res->res); + pci_dev_res_free_list(&save_head); goto out; } @@ -683,13 +680,13 @@ assign: list_for_each_entry(save_res, &save_head, list) { struct resource *res = save_res->res; - if (res->parent) + if (resource_assigned(res)) continue; - restore_dev_resource(save_res); + pci_dev_res_restore(save_res); } - free_list(&local_fail_head); - free_list(&save_head); + pci_dev_res_free_list(&local_fail_head); + pci_dev_res_free_list(&save_head); goto out; } @@ -699,28 +696,29 @@ assign: list_for_each_entry_safe(dev_res, tmp_res, head, list) { res = dev_res->res; - if (res->parent && !pci_need_to_release(fail_type, res)) { + if (resource_assigned(res) && + !pci_need_to_release(fail_type, res)) { /* Remove it from realloc_head list */ - remove_from_list(realloc_head, res); - remove_from_list(&save_head, res); + pci_dev_res_remove_from_list(realloc_head, res); + pci_dev_res_remove_from_list(&save_head, res); list_del(&dev_res->list); kfree(dev_res); } } - free_list(&local_fail_head); + pci_dev_res_free_list(&local_fail_head); /* Release assigned resource */ list_for_each_entry(dev_res, head, list) { res = dev_res->res; dev = dev_res->dev; pci_release_resource(dev, pci_resource_num(dev, res)); - restore_dev_resource(dev_res); + pci_dev_res_restore(dev_res); } /* Restore start/end/flags from saved list */ list_for_each_entry(save_res, &save_head, list) - restore_dev_resource(save_res); - free_list(&save_head); + pci_dev_res_restore(save_res); + pci_dev_res_free_list(&save_head); /* Satisfy the must-have resource requests */ assign_requested_resources_sorted(head, NULL, false); @@ -735,19 +733,19 @@ out: res = dev_res->res; dev = dev_res->dev; - if (res->parent) + if (resource_assigned(res)) continue; if (fail_head) { - add_to_list(fail_head, dev, res, - 0 /* don't care */, - 0 /* don't care */); + pci_dev_res_add_to_list(fail_head, dev, res, + 0 /* don't care */, + 0 /* don't care */); } reset_resource(dev, res); } - free_list(head); + pci_dev_res_free_list(head); } static void pdev_assign_resources_sorted(struct pci_dev *dev, @@ -774,61 +772,6 @@ static void pbus_assign_resources_sorted(const struct pci_bus *bus, __assign_resources_sorted(&head, realloc_head, fail_head); } -void pci_setup_cardbus(struct pci_bus *bus) -{ - struct pci_dev *bridge = bus->self; - struct resource *res; - struct pci_bus_region region; - - pci_info(bridge, "CardBus bridge to %pR\n", - &bus->busn_res); - - res = bus->resource[0]; - pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->parent && res->flags & IORESOURCE_IO) { - /* - * The IO resource is allocated a range twice as large as it - * would normally need. This allows us to set both IO regs. - */ - pci_info(bridge, " bridge window %pR\n", res); - pci_write_config_dword(bridge, PCI_CB_IO_BASE_0, - region.start); - pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_0, - region.end); - } - - res = bus->resource[1]; - pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->parent && res->flags & IORESOURCE_IO) { - pci_info(bridge, " bridge window %pR\n", res); - pci_write_config_dword(bridge, PCI_CB_IO_BASE_1, - region.start); - pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_1, - region.end); - } - - res = bus->resource[2]; - pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->parent && res->flags & IORESOURCE_MEM) { - pci_info(bridge, " bridge window %pR\n", res); - pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0, - region.start); - pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_0, - region.end); - } - - res = bus->resource[3]; - pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->parent && res->flags & IORESOURCE_MEM) { - pci_info(bridge, " bridge window %pR\n", res); - pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1, - region.start); - pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_1, - region.end); - } -} -EXPORT_SYMBOL(pci_setup_cardbus); - /* * Initialize bridges with base/limit values we have collected. PCI-to-PCI * Bridge Architecture Specification rev. 1.1 (1998) requires that if there @@ -860,7 +803,7 @@ static void pci_setup_bridge_io(struct pci_dev *bridge) res = &bridge->resource[PCI_BRIDGE_IO_WINDOW]; res_name = pci_resource_name(bridge, PCI_BRIDGE_IO_WINDOW); pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->parent && res->flags & IORESOURCE_IO) { + if (resource_assigned(res) && res->flags & IORESOURCE_IO) { pci_read_config_word(bridge, PCI_IO_BASE, &l); io_base_lo = (region.start >> 8) & io_mask; io_limit_lo = (region.end >> 8) & io_mask; @@ -892,7 +835,7 @@ static void pci_setup_bridge_mmio(struct pci_dev *bridge) res = &bridge->resource[PCI_BRIDGE_MEM_WINDOW]; res_name = pci_resource_name(bridge, PCI_BRIDGE_MEM_WINDOW); pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->parent && res->flags & IORESOURCE_MEM) { + if (resource_assigned(res) && res->flags & IORESOURCE_MEM) { l = (region.start >> 16) & 0xfff0; l |= region.end & 0xfff00000; pci_info(bridge, " %s %pR\n", res_name, res); @@ -921,7 +864,7 @@ static void pci_setup_bridge_mmio_pref(struct pci_dev *bridge) res = &bridge->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; res_name = pci_resource_name(bridge, PCI_BRIDGE_PREF_MEM_WINDOW); pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->parent && res->flags & IORESOURCE_PREFETCH) { + if (resource_assigned(res) && res->flags & IORESOURCE_PREFETCH) { l = (region.start >> 16) & 0xfff0; l |= region.end & 0xfff00000; if (res->flags & IORESOURCE_MEM_64) { @@ -992,7 +935,7 @@ int pci_claim_bridge_resource(struct pci_dev *bridge, int i) { int ret = -EINVAL; - if (i < PCI_BRIDGE_RESOURCES || i > PCI_BRIDGE_RESOURCE_END) + if (!pci_resource_is_bridge_win(i)) return 0; if (pci_claim_resource(bridge, i) == 0) @@ -1068,18 +1011,11 @@ static resource_size_t calculate_iosize(resource_size_t size, static resource_size_t calculate_memsize(resource_size_t size, resource_size_t min_size, - resource_size_t add_size, resource_size_t children_add_size, - resource_size_t old_size, resource_size_t align) { - if (size < min_size) - size = min_size; - if (old_size == 1) - old_size = 0; - - size = max(size, add_size) + children_add_size; - return ALIGN(max(size, old_size), align); + size = max(size, min_size) + children_add_size; + return ALIGN(size, align); } resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus, @@ -1117,8 +1053,7 @@ static resource_size_t window_alignment(struct pci_bus *bus, unsigned long type) * pbus_size_io() - Size the I/O window of a given bus * * @bus: The bus - * @min_size: The minimum I/O window that must be allocated - * @add_size: Additional optional I/O window + * @add_size: Additional I/O window * @realloc_head: Track the additional I/O window on this list * * Sizing the I/O windows of the PCI-PCI bridge is trivial, since these @@ -1126,8 +1061,7 @@ static resource_size_t window_alignment(struct pci_bus *bus, unsigned long type) * devices are limited to 256 bytes. We must be careful with the ISA * aliasing though. */ -static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, - resource_size_t add_size, +static void pbus_size_io(struct pci_bus *bus, resource_size_t add_size, struct list_head *realloc_head) { struct pci_dev *dev; @@ -1140,7 +1074,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, return; /* If resource is already assigned, nothing more to do */ - if (b_res->parent) + if (resource_assigned(b_res)) return; min_align = window_alignment(bus, IORESOURCE_IO); @@ -1150,7 +1084,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, pci_dev_for_each_resource(dev, r) { unsigned long r_size; - if (r->parent || !(r->flags & IORESOURCE_IO)) + if (resource_assigned(r) || !(r->flags & IORESOURCE_IO)) continue; if (!pdev_resource_assignable(dev, r)) @@ -1172,7 +1106,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, } } - size0 = calculate_iosize(size, min_size, size1, 0, 0, + size0 = calculate_iosize(size, realloc_head ? 0 : add_size, size1, 0, 0, resource_size(b_res), min_align); if (size0) @@ -1180,7 +1114,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, size1 = size0; if (realloc_head && (add_size > 0 || children_add_size > 0)) { - size1 = calculate_iosize(size, min_size, size1, add_size, + size1 = calculate_iosize(size, 0, size1, add_size, children_add_size, resource_size(b_res), min_align); } @@ -1197,8 +1131,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, b_res->flags |= IORESOURCE_STARTALIGN; if (bus->self && size1 > size0 && realloc_head) { b_res->flags &= ~IORESOURCE_DISABLED; - add_to_list(realloc_head, bus->self, b_res, size1-size0, - min_align); + pci_dev_res_add_to_list(realloc_head, bus->self, b_res, + size1 - size0, min_align); pci_info(bus->self, "bridge window %pR to %pR add_size %llx\n", b_res, &bus->busn_res, (unsigned long long) size1 - size0); @@ -1227,65 +1161,90 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns, return min_align; } -/** - * pbus_upstream_space_available - Check no upstream resource limits allocation - * @bus: The bus - * @res: The resource to help select the correct bridge window - * @size: The size required from the bridge window - * @align: Required alignment for the resource - * - * Check that @size can fit inside the upstream bridge resources that are - * already assigned. Select the upstream bridge window based on the type of - * @res. - * - * Return: %true if enough space is available on all assigned upstream +/* + * Calculate bridge window head alignment that leaves no gaps in between * resources. */ -static bool pbus_upstream_space_available(struct pci_bus *bus, - struct resource *res, - resource_size_t size, - resource_size_t align) -{ - struct resource_constraint constraint = { - .max = RESOURCE_SIZE_MAX, - .align = align, - }; - struct pci_bus *downstream = bus; - - while ((bus = bus->parent)) { - if (pci_is_root_bus(bus)) - break; +static resource_size_t calculate_head_align(resource_size_t *aligns, + int max_order) +{ + resource_size_t head_align = 1; + resource_size_t remainder = 0; + int order; - res = pbus_select_window(bus, res); - if (!res) - return false; - if (!res->parent) - continue; + /* Take the largest alignment as the starting point. */ + head_align <<= max_order + __ffs(SZ_1M); - if (resource_size(res) >= size) { - struct resource gap = {}; + for (order = max_order - 1; order >= 0; order--) { + resource_size_t align1 = 1; - if (find_resource_space(res, &gap, size, &constraint) == 0) { - gap.flags = res->flags; - pci_dbg(bus->self, - "Assigned bridge window %pR to %pR free space at %pR\n", - res, &bus->busn_res, &gap); - return true; - } + align1 <<= order + __ffs(SZ_1M); + + /* + * Account smaller resources with alignment < max_order that + * could be used to fill head room if alignment less than + * max_order is used. + */ + remainder += aligns[order]; + + /* + * Test if head fill is enough to satisfy the alignment of + * the larger resources after reducing the alignment. + */ + while ((head_align > align1) && (remainder >= head_align / 2)) { + head_align /= 2; + remainder -= head_align; } + } + + return head_align; +} + +/* + * pbus_size_mem_optional - Account optional resources in bridge window + * + * Account an optional resource or the optional part of the resource in bridge + * window size. + * + * Return: %true if the resource is entirely optional. + */ +static bool pbus_size_mem_optional(struct pci_dev *dev, int resno, + resource_size_t align, + struct list_head *realloc_head, + resource_size_t *add_align, + resource_size_t *children_add_size) +{ + struct resource *res = pci_resource_n(dev, resno); + bool optional = pci_resource_is_optional(dev, resno); + resource_size_t r_size = resource_size(res); + struct pci_dev_resource *dev_res; - if (bus->self) { - pci_info(bus->self, - "Assigned bridge window %pR to %pR cannot fit 0x%llx required for %s bridging to %pR\n", - res, &bus->busn_res, - (unsigned long long)size, - pci_name(downstream->self), - &downstream->busn_res); + if (!realloc_head) + return false; + + if (!optional) { + /* + * Only bridges have optional sizes in realloc_head at this + * point. As res_to_dev_res() walks the entire realloc_head + * list, skip calling it when known unnecessary. + */ + if (!pci_resource_is_bridge_win(resno)) + return false; + + dev_res = res_to_dev_res(realloc_head, res); + if (dev_res) { + *children_add_size += dev_res->add_size; + *add_align = max(*add_align, dev_res->min_align); } return false; } + /* Put SRIOV requested res to the optional list */ + pci_dev_res_add_to_list(realloc_head, dev, res, 0, align); + *children_add_size += r_size; + *add_align = max(align, *add_align); + return true; } @@ -1293,12 +1252,11 @@ static bool pbus_upstream_space_available(struct pci_bus *bus, * pbus_size_mem() - Size the memory window of a given bus * * @bus: The bus - * @type: The type of bridge resource - * @min_size: The minimum memory window that must be allocated - * @add_size: Additional optional memory window + * @b_res: The bridge window resource + * @add_size: Additional memory window * @realloc_head: Track the additional memory window on this list * - * Calculate the size of the bus resource for @type and minimal alignment + * Calculate the size of the bridge window @b_res and minimal alignment * which guarantees that all child resources fit in this size. * * Set the bus resource start/end to indicate the required size if there an @@ -1307,30 +1265,24 @@ static bool pbus_upstream_space_available(struct pci_bus *bus, * Add optional resource requests to the @realloc_head list if it is * supplied. */ -static void pbus_size_mem(struct pci_bus *bus, unsigned long type, - resource_size_t min_size, - resource_size_t add_size, - struct list_head *realloc_head) +static void pbus_size_mem(struct pci_bus *bus, struct resource *b_res, + resource_size_t add_size, + struct list_head *realloc_head) { struct pci_dev *dev; resource_size_t min_align, win_align, align, size, size0, size1 = 0; - resource_size_t aligns[28]; /* Alignments from 1MB to 128TB */ + resource_size_t aligns[28] = {}; /* Alignments from 1MB to 128TB */ int order, max_order; - struct resource *b_res = pbus_select_window_for_type(bus, type); resource_size_t children_add_size = 0; - resource_size_t children_add_align = 0; resource_size_t add_align = 0; - resource_size_t relaxed_align; - resource_size_t old_size; if (!b_res) return; /* If resource is already assigned, nothing more to do */ - if (b_res->parent) + if (resource_assigned(b_res)) return; - memset(aligns, 0, sizeof(aligns)); max_order = 0; size = 0; @@ -1348,88 +1300,50 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type, if (b_res != pbus_select_window(bus, r)) continue; - r_size = resource_size(r); - - /* Put SRIOV requested res to the optional list */ - if (realloc_head && pci_resource_is_optional(dev, i)) { - add_align = max(pci_resource_alignment(dev, r), add_align); - add_to_list(realloc_head, dev, r, 0, 0 /* Don't care */); - children_add_size += r_size; - continue; - } - + align = pci_resource_alignment(dev, r); /* * aligns[0] is for 1MB (since bridge memory * windows are always at least 1MB aligned), so * keep "order" from being negative for smaller * resources. */ - align = pci_resource_alignment(dev, r); - order = __ffs(align) - __ffs(SZ_1M); - if (order < 0) - order = 0; + order = max_t(int, __ffs(align) - __ffs(SZ_1M), 0); if (order >= ARRAY_SIZE(aligns)) { pci_warn(dev, "%s %pR: disabling; bad alignment %#llx\n", r_name, r, (unsigned long long) align); r->flags = 0; continue; } + + if (pbus_size_mem_optional(dev, i, align, + realloc_head, &add_align, + &children_add_size)) + continue; + + r_size = resource_size(r); size += max(r_size, align); - /* - * Exclude ranges with size > align from calculation of - * the alignment. - */ - if (r_size <= align) - aligns[order] += align; + + aligns[order] += align; if (order > max_order) max_order = order; - - if (realloc_head) { - children_add_size += get_res_add_size(realloc_head, r); - children_add_align = get_res_add_align(realloc_head, r); - add_align = max(add_align, children_add_align); - } } } - old_size = resource_size(b_res); win_align = window_alignment(bus, b_res->flags); - min_align = calculate_mem_align(aligns, max_order); + min_align = calculate_head_align(aligns, max_order); min_align = max(min_align, win_align); - size0 = calculate_memsize(size, min_size, 0, 0, old_size, min_align); + size0 = calculate_memsize(size, realloc_head ? 0 : add_size, + 0, win_align); if (size0) { resource_set_range(b_res, min_align, size0); b_res->flags &= ~IORESOURCE_DISABLED; } - if (bus->self && size0 && - !pbus_upstream_space_available(bus, b_res, size0, min_align)) { - relaxed_align = 1ULL << (max_order + __ffs(SZ_1M)); - relaxed_align = max(relaxed_align, win_align); - min_align = min(min_align, relaxed_align); - size0 = calculate_memsize(size, min_size, 0, 0, old_size, win_align); - resource_set_range(b_res, min_align, size0); - pci_info(bus->self, "bridge window %pR to %pR requires relaxed alignment rules\n", - b_res, &bus->busn_res); - } - if (realloc_head && (add_size > 0 || children_add_size > 0)) { add_align = max(min_align, add_align); - size1 = calculate_memsize(size, min_size, add_size, children_add_size, - old_size, add_align); - - if (bus->self && size1 && - !pbus_upstream_space_available(bus, b_res, size1, add_align)) { - relaxed_align = 1ULL << (max_order + __ffs(SZ_1M)); - relaxed_align = max(relaxed_align, win_align); - min_align = min(min_align, relaxed_align); - size1 = calculate_memsize(size, min_size, add_size, children_add_size, - old_size, win_align); - pci_info(bus->self, - "bridge window %pR to %pR requires relaxed alignment rules\n", - b_res, &bus->busn_res); - } + size1 = calculate_memsize(size, add_size, children_add_size, + win_align); } if (!size0 && !size1) { @@ -1442,121 +1356,24 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type, resource_set_range(b_res, min_align, size0); b_res->flags |= IORESOURCE_STARTALIGN; - if (bus->self && size1 > size0 && realloc_head) { + if (bus->self && realloc_head && (size1 > size0 || add_align > min_align)) { b_res->flags &= ~IORESOURCE_DISABLED; - add_to_list(realloc_head, bus->self, b_res, size1-size0, add_align); + add_size = size1 > size0 ? size1 - size0 : 0; + pci_dev_res_add_to_list(realloc_head, bus->self, b_res, + add_size, add_align); pci_info(bus->self, "bridge window %pR to %pR add_size %llx add_align %llx\n", b_res, &bus->busn_res, - (unsigned long long) (size1 - size0), + (unsigned long long) add_size, (unsigned long long) add_align); } } -unsigned long pci_cardbus_resource_alignment(struct resource *res) -{ - if (res->flags & IORESOURCE_IO) - return pci_cardbus_io_size; - if (res->flags & IORESOURCE_MEM) - return pci_cardbus_mem_size; - return 0; -} - -static void pci_bus_size_cardbus(struct pci_bus *bus, - struct list_head *realloc_head) -{ - struct pci_dev *bridge = bus->self; - struct resource *b_res; - resource_size_t b_res_3_size = pci_cardbus_mem_size * 2; - u16 ctrl; - - b_res = &bridge->resource[PCI_CB_BRIDGE_IO_0_WINDOW]; - if (b_res->parent) - goto handle_b_res_1; - /* - * Reserve some resources for CardBus. We reserve a fixed amount - * of bus space for CardBus bridges. - */ - resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size); - b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; - if (realloc_head) { - b_res->end -= pci_cardbus_io_size; - add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size, - pci_cardbus_io_size); - } - -handle_b_res_1: - b_res = &bridge->resource[PCI_CB_BRIDGE_IO_1_WINDOW]; - if (b_res->parent) - goto handle_b_res_2; - resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size); - b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; - if (realloc_head) { - b_res->end -= pci_cardbus_io_size; - add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size, - pci_cardbus_io_size); - } - -handle_b_res_2: - /* MEM1 must not be pref MMIO */ - pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); - if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM1) { - ctrl &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1; - pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl); - pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); - } - - /* Check whether prefetchable memory is supported by this bridge. */ - pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); - if (!(ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0)) { - ctrl |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM0; - pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl); - pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); - } - - b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_0_WINDOW]; - if (b_res->parent) - goto handle_b_res_3; - /* - * If we have prefetchable memory support, allocate two regions. - * Otherwise, allocate one region of twice the size. - */ - if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) { - resource_set_range(b_res, pci_cardbus_mem_size, - pci_cardbus_mem_size); - b_res->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH | - IORESOURCE_STARTALIGN; - if (realloc_head) { - b_res->end -= pci_cardbus_mem_size; - add_to_list(realloc_head, bridge, b_res, - pci_cardbus_mem_size, pci_cardbus_mem_size); - } - - /* Reduce that to half */ - b_res_3_size = pci_cardbus_mem_size; - } - -handle_b_res_3: - b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_1_WINDOW]; - if (b_res->parent) - goto handle_done; - resource_set_range(b_res, pci_cardbus_mem_size, b_res_3_size); - b_res->flags |= IORESOURCE_MEM | IORESOURCE_STARTALIGN; - if (realloc_head) { - b_res->end -= b_res_3_size; - add_to_list(realloc_head, bridge, b_res, b_res_3_size, - pci_cardbus_mem_size); - } - -handle_done: - ; -} - void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) { struct pci_dev *dev; resource_size_t additional_io_size = 0, additional_mmio_size = 0, additional_mmio_pref_size = 0; - struct resource *pref; + struct resource *b_res; struct pci_host_bridge *host; int hdr_type; @@ -1567,7 +1384,8 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) switch (dev->hdr_type) { case PCI_HEADER_TYPE_CARDBUS: - pci_bus_size_cardbus(b, realloc_head); + if (pci_bus_size_cardbus_bridge(b, realloc_head)) + continue; break; case PCI_HEADER_TYPE_BRIDGE: @@ -1582,12 +1400,8 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) host = to_pci_host_bridge(bus->bridge); if (!host->size_windows) return; - pci_bus_for_each_resource(bus, pref) - if (pref && (pref->flags & IORESOURCE_PREFETCH)) - break; hdr_type = -1; /* Intentionally invalid - not a PCI device. */ } else { - pref = &bus->self->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; hdr_type = bus->self->hdr_type; } @@ -1605,20 +1419,21 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) } fallthrough; default: - pbus_size_io(bus, realloc_head ? 0 : additional_io_size, - additional_io_size, realloc_head); - - if (pref && (pref->flags & IORESOURCE_PREFETCH)) { - pbus_size_mem(bus, - IORESOURCE_MEM | IORESOURCE_PREFETCH | - (pref->flags & IORESOURCE_MEM_64), - realloc_head ? 0 : additional_mmio_pref_size, - additional_mmio_pref_size, realloc_head); + pbus_size_io(bus, additional_io_size, realloc_head); + + b_res = pbus_select_window_for_type(bus, IORESOURCE_MEM | + IORESOURCE_PREFETCH | + IORESOURCE_MEM_64); + if (b_res && (b_res->flags & IORESOURCE_PREFETCH)) { + pbus_size_mem(bus, b_res, additional_mmio_pref_size, + realloc_head); } - pbus_size_mem(bus, IORESOURCE_MEM, - realloc_head ? 0 : additional_mmio_size, - additional_mmio_size, realloc_head); + b_res = pbus_select_window_for_type(bus, IORESOURCE_MEM); + if (b_res) { + pbus_size_mem(bus, b_res, additional_mmio_size, + realloc_head); + } break; } } @@ -1656,12 +1471,13 @@ static void pdev_assign_fixed_resources(struct pci_dev *dev) pci_dev_for_each_resource(dev, r) { struct pci_bus *b; - if (r->parent || !(r->flags & IORESOURCE_PCI_FIXED) || + if (resource_assigned(r) || + !(r->flags & IORESOURCE_PCI_FIXED) || !(r->flags & (IORESOURCE_IO | IORESOURCE_MEM))) continue; b = dev->bus; - while (b && !r->parent) { + while (b && !resource_assigned(r)) { assign_fixed_resource_on_bus(b, r); b = b->parent; } @@ -1693,7 +1509,7 @@ void __pci_bus_assign_resources(const struct pci_bus *bus, break; case PCI_HEADER_TYPE_CARDBUS: - pci_setup_cardbus(b); + pci_setup_cardbus_bridge(b); break; default: @@ -1717,7 +1533,7 @@ static void pci_claim_device_resources(struct pci_dev *dev) for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) { struct resource *r = &dev->resource[i]; - if (!r->flags || r->parent) + if (!r->flags || resource_assigned(r)) continue; pci_claim_resource(dev, i); @@ -1731,7 +1547,9 @@ static void pci_claim_bridge_resources(struct pci_dev *dev) for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { struct resource *r = &dev->resource[i]; - if (!r->flags || r->parent) + if (!r->flags || resource_assigned(r)) + continue; + if (r->flags & IORESOURCE_DISABLED) continue; pci_claim_bridge_resource(dev, i); @@ -1798,7 +1616,7 @@ static void __pci_bridge_assign_resources(const struct pci_dev *bridge, break; case PCI_CLASS_BRIDGE_CARDBUS: - pci_setup_cardbus(b); + pci_setup_cardbus_bridge(b); break; default: @@ -1814,7 +1632,7 @@ static void pci_bridge_release_resources(struct pci_bus *bus, struct pci_dev *dev = bus->self; int idx, ret; - if (!b_win->parent) + if (!resource_assigned(b_win)) return; idx = pci_resource_num(dev, b_win); @@ -2010,7 +1828,7 @@ static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res, { resource_size_t add_size, size = resource_size(res); - if (res->parent) + if (resource_assigned(res)) return; if (!new_size) @@ -2032,7 +1850,7 @@ static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res, /* If the resource is part of the add_list, remove it now */ if (add_list) - remove_from_list(add_list, res); + pci_dev_res_remove_from_list(add_list, res); } static void remove_dev_resource(struct resource *avail, struct pci_dev *dev, @@ -2100,7 +1918,7 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, * window. */ align = pci_resource_alignment(bridge, res); - if (!res->parent && align) + if (!resource_assigned(res) && align) available[i].start = min(ALIGN(available[i].start, align), available[i].end + 1); @@ -2284,9 +2102,9 @@ static void pci_prepare_next_assign_round(struct list_head *fail_head, /* Restore size and flags */ list_for_each_entry(fail_res, fail_head, list) - restore_dev_resource(fail_res); + pci_dev_res_restore(fail_res); - free_list(fail_head); + pci_dev_res_free_list(fail_head); } /* @@ -2333,7 +2151,7 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) /* Depth last, allocate resources and update the hardware. */ __pci_bus_assign_resources(bus, add_list, &fail_head); if (WARN_ON_ONCE(add_list && !list_empty(add_list))) - free_list(add_list); + pci_dev_res_free_list(add_list); tried_times++; /* Any device complain? */ @@ -2348,7 +2166,7 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) dev_info(&bus->dev, "Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n"); } - free_list(&fail_head); + pci_dev_res_free_list(&fail_head); break; } @@ -2396,7 +2214,7 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) __pci_bridge_assign_resources(bridge, &add_list, &fail_head); if (WARN_ON_ONCE(!list_empty(&add_list))) - free_list(&add_list); + pci_dev_res_free_list(&add_list); tried_times++; if (list_empty(&fail_head)) @@ -2404,7 +2222,7 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) if (tried_times >= 2) { /* Still fail, don't need to try more */ - free_list(&fail_head); + pci_dev_res_free_list(&fail_head); break; } @@ -2445,7 +2263,7 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource * /* Ignore BARs which are still in use */ if (!res->child) { - ret = add_to_list(saved, bridge, res, 0, 0); + ret = pci_dev_res_add_to_list(saved, bridge, res, 0, 0); if (ret) return ret; @@ -2467,12 +2285,12 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource * __pci_bus_size_bridges(bridge->subordinate, &added); __pci_bridge_assign_resources(bridge, &added, &failed); if (WARN_ON_ONCE(!list_empty(&added))) - free_list(&added); + pci_dev_res_free_list(&added); if (!list_empty(&failed)) { if (pci_required_resource_failed(&failed, type)) ret = -ENOSPC; - free_list(&failed); + pci_dev_res_free_list(&failed); if (ret) return ret; @@ -2520,7 +2338,7 @@ int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size if (b_win != pbus_select_window(bus, r)) continue; - ret = add_to_list(&saved, pdev, r, 0, 0); + ret = pci_dev_res_add_to_list(&saved, pdev, r, 0, 0); if (ret) goto restore; pci_release_resource(pdev, i); @@ -2538,7 +2356,7 @@ int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size out: up_read(&pci_bus_sem); - free_list(&saved); + pci_dev_res_free_list(&saved); return ret; restore: @@ -2549,12 +2367,12 @@ restore: i = pci_resource_num(dev, res); - if (res->parent) { + if (resource_assigned(res)) { release_child_resources(res); pci_release_resource(dev, i); } - restore_dev_resource(dev_res); + pci_dev_res_restore(dev_res); ret = pci_claim_resource(dev, i); if (ret) @@ -2586,6 +2404,6 @@ void pci_assign_unassigned_bus_resources(struct pci_bus *bus) up_read(&pci_bus_sem); __pci_bus_assign_resources(bus, &add_list, NULL); if (WARN_ON_ONCE(!list_empty(&add_list))) - free_list(&add_list); + pci_dev_res_free_list(&add_list); } EXPORT_SYMBOL_GPL(pci_assign_unassigned_bus_resources); diff --git a/drivers/pci/setup-cardbus.c b/drivers/pci/setup-cardbus.c new file mode 100644 index 000000000000..1ebd13a1f730 --- /dev/null +++ b/drivers/pci/setup-cardbus.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cardbus bridge setup routines. + */ + +#include <linux/bitfield.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/sizes.h> +#include <linux/sprintf.h> +#include <linux/types.h> + +#include "pci.h" + +#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ +#define CARDBUS_RESERVE_BUSNR 3 + +#define DEFAULT_CARDBUS_IO_SIZE SZ_256 +#define DEFAULT_CARDBUS_MEM_SIZE SZ_64M +/* pci=cbmemsize=nnM,cbiosize=nn can override this */ +static unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE; +static unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE; + +unsigned long pci_cardbus_resource_alignment(struct resource *res) +{ + if (res->flags & IORESOURCE_IO) + return pci_cardbus_io_size; + if (res->flags & IORESOURCE_MEM) + return pci_cardbus_mem_size; + return 0; +} + +int pci_bus_size_cardbus_bridge(struct pci_bus *bus, + struct list_head *realloc_head) +{ + struct pci_dev *bridge = bus->self; + struct resource *b_res; + resource_size_t b_res_3_size = pci_cardbus_mem_size * 2; + u16 ctrl; + + b_res = &bridge->resource[PCI_CB_BRIDGE_IO_0_WINDOW]; + if (resource_assigned(b_res)) + goto handle_b_res_1; + /* + * Reserve some resources for CardBus. We reserve a fixed amount + * of bus space for CardBus bridges. + */ + resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size); + b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; + if (realloc_head) { + b_res->end -= pci_cardbus_io_size; + pci_dev_res_add_to_list(realloc_head, bridge, b_res, + pci_cardbus_io_size, + pci_cardbus_io_size); + } + +handle_b_res_1: + b_res = &bridge->resource[PCI_CB_BRIDGE_IO_1_WINDOW]; + if (resource_assigned(b_res)) + goto handle_b_res_2; + resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size); + b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; + if (realloc_head) { + b_res->end -= pci_cardbus_io_size; + pci_dev_res_add_to_list(realloc_head, bridge, b_res, + pci_cardbus_io_size, + pci_cardbus_io_size); + } + +handle_b_res_2: + /* MEM1 must not be pref MMIO */ + pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); + if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM1) { + ctrl &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1; + pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl); + pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); + } + + /* Check whether prefetchable memory is supported by this bridge. */ + pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); + if (!(ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0)) { + ctrl |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM0; + pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl); + pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); + } + + b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_0_WINDOW]; + if (resource_assigned(b_res)) + goto handle_b_res_3; + /* + * If we have prefetchable memory support, allocate two regions. + * Otherwise, allocate one region of twice the size. + */ + if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) { + resource_set_range(b_res, pci_cardbus_mem_size, + pci_cardbus_mem_size); + b_res->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH | + IORESOURCE_STARTALIGN; + if (realloc_head) { + b_res->end -= pci_cardbus_mem_size; + pci_dev_res_add_to_list(realloc_head, bridge, b_res, + pci_cardbus_mem_size, + pci_cardbus_mem_size); + } + + /* Reduce that to half */ + b_res_3_size = pci_cardbus_mem_size; + } + +handle_b_res_3: + b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_1_WINDOW]; + if (resource_assigned(b_res)) + goto handle_done; + resource_set_range(b_res, pci_cardbus_mem_size, b_res_3_size); + b_res->flags |= IORESOURCE_MEM | IORESOURCE_STARTALIGN; + if (realloc_head) { + b_res->end -= b_res_3_size; + pci_dev_res_add_to_list(realloc_head, bridge, b_res, + b_res_3_size, pci_cardbus_mem_size); + } + +handle_done: + return 0; +} + +void pci_setup_cardbus_bridge(struct pci_bus *bus) +{ + struct pci_dev *bridge = bus->self; + struct resource *res; + struct pci_bus_region region; + + pci_info(bridge, "CardBus bridge to %pR\n", + &bus->busn_res); + + res = bus->resource[0]; + pcibios_resource_to_bus(bridge->bus, ®ion, res); + if (resource_assigned(res) && res->flags & IORESOURCE_IO) { + /* + * The IO resource is allocated a range twice as large as it + * would normally need. This allows us to set both IO regs. + */ + pci_info(bridge, " bridge window %pR\n", res); + pci_write_config_dword(bridge, PCI_CB_IO_BASE_0, + region.start); + pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_0, + region.end); + } + + res = bus->resource[1]; + pcibios_resource_to_bus(bridge->bus, ®ion, res); + if (resource_assigned(res) && res->flags & IORESOURCE_IO) { + pci_info(bridge, " bridge window %pR\n", res); + pci_write_config_dword(bridge, PCI_CB_IO_BASE_1, + region.start); + pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_1, + region.end); + } + + res = bus->resource[2]; + pcibios_resource_to_bus(bridge->bus, ®ion, res); + if (resource_assigned(res) && res->flags & IORESOURCE_MEM) { + pci_info(bridge, " bridge window %pR\n", res); + pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0, + region.start); + pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_0, + region.end); + } + + res = bus->resource[3]; + pcibios_resource_to_bus(bridge->bus, ®ion, res); + if (resource_assigned(res) && res->flags & IORESOURCE_MEM) { + pci_info(bridge, " bridge window %pR\n", res); + pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1, + region.start); + pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_1, + region.end); + } +} +EXPORT_SYMBOL(pci_setup_cardbus_bridge); + +int pci_setup_cardbus(char *str) +{ + if (!strncmp(str, "cbiosize=", 9)) { + pci_cardbus_io_size = memparse(str + 9, &str); + return 0; + } else if (!strncmp(str, "cbmemsize=", 10)) { + pci_cardbus_mem_size = memparse(str + 10, &str); + return 0; + } + + return -ENOENT; +} + +int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, + u32 buses, int max, + unsigned int available_buses, int pass) +{ + struct pci_bus *child; + bool fixed_buses; + u8 fixed_sec, fixed_sub; + int next_busnr; + u32 i, j = 0; + + /* + * We need to assign a number to this bus which we always do in the + * second pass. + */ + if (!pass) { + /* + * Temporarily disable forwarding of the configuration + * cycles on all bridges in this bus segment to avoid + * possible conflicts in the second pass between two bridges + * programmed with overlapping bus ranges. + */ + pci_write_config_dword(dev, PCI_PRIMARY_BUS, + buses & PCI_SEC_LATENCY_TIMER_MASK); + return max; + } + + /* Clear errors */ + pci_write_config_word(dev, PCI_STATUS, 0xffff); + + /* Read bus numbers from EA Capability (if present) */ + fixed_buses = pci_ea_fixed_busnrs(dev, &fixed_sec, &fixed_sub); + if (fixed_buses) + next_busnr = fixed_sec; + else + next_busnr = max + 1; + + /* + * Prevent assigning a bus number that already exists. This can + * happen when a bridge is hot-plugged, so in this case we only + * re-scan this bus. + */ + child = pci_find_bus(pci_domain_nr(bus), next_busnr); + if (!child) { + child = pci_add_new_bus(bus, dev, next_busnr); + if (!child) + return max; + pci_bus_insert_busn_res(child, next_busnr, bus->busn_res.end); + } + max++; + if (available_buses) + available_buses--; + + buses = (buses & PCI_SEC_LATENCY_TIMER_MASK) | + FIELD_PREP(PCI_PRIMARY_BUS_MASK, child->primary) | + FIELD_PREP(PCI_SECONDARY_BUS_MASK, child->busn_res.start) | + FIELD_PREP(PCI_SUBORDINATE_BUS_MASK, child->busn_res.end); + + /* + * yenta.c forces a secondary latency timer of 176. + * Copy that behaviour here. + */ + buses &= ~PCI_SEC_LATENCY_TIMER_MASK; + buses |= FIELD_PREP(PCI_SEC_LATENCY_TIMER_MASK, CARDBUS_LATENCY_TIMER); + + /* We need to blast all three values with a single write */ + pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses); + + /* + * For CardBus bridges, we leave 4 bus numbers as cards with a + * PCI-to-PCI bridge can be inserted later. + */ + for (i = 0; i < CARDBUS_RESERVE_BUSNR; i++) { + struct pci_bus *parent = bus; + + if (pci_find_bus(pci_domain_nr(bus), max + i + 1)) + break; + + while (parent->parent) { + if (!pcibios_assign_all_busses() && + (parent->busn_res.end > max) && + (parent->busn_res.end <= max + i)) { + j = 1; + } + parent = parent->parent; + } + if (j) { + /* + * Often, there are two CardBus bridges -- try to + * leave one valid bus number for each one. + */ + i /= 2; + break; + } + } + max += i; + + /* + * Set subordinate bus number to its real value. If fixed + * subordinate bus number exists from EA capability then use it. + */ + if (fixed_buses) + max = fixed_sub; + pci_bus_update_busn_res_end(child, max); + pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); + + scnprintf(child->name, sizeof(child->name), "PCI CardBus %04x:%02x", + pci_domain_nr(bus), child->number); + + pbus_validate_busn(child); + + return max; +} diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index e5fcadfc58b0..bb2aef373d6f 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -359,7 +359,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) res->flags &= ~IORESOURCE_UNSET; res->flags &= ~IORESOURCE_STARTALIGN; - if (resno >= PCI_BRIDGE_RESOURCES && resno <= PCI_BRIDGE_RESOURCE_END) + if (pci_resource_is_bridge_win(resno)) res->flags &= ~IORESOURCE_DISABLED; pci_info(dev, "%s %pR: assigned\n", res_name, res); diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 923ed23570a0..34c4eaee7dfc 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -779,7 +779,7 @@ static void yenta_allocate_resources(struct yenta_socket *socket) IORESOURCE_MEM, PCI_CB_MEMORY_BASE_1, PCI_CB_MEMORY_LIMIT_1); if (program) - pci_setup_cardbus(socket->dev->subordinate); + pci_setup_cardbus_bridge(socket->dev->subordinate); } diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 9afa30f9346f..60ca6a49839c 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -338,7 +338,7 @@ static inline bool resource_union(const struct resource *r1, const struct resour * Check if this resource is added to a resource tree or detached. Caller is * responsible for not racing assignment. */ -static inline bool resource_assigned(struct resource *res) +static inline bool resource_assigned(const struct resource *res) { return res->parent; } diff --git a/include/linux/pci.h b/include/linux/pci.h index dd46f3d38e20..fd26d5d2c8bb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1248,7 +1248,11 @@ void pci_stop_and_remove_bus_device(struct pci_dev *dev); void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev); void pci_stop_root_bus(struct pci_bus *bus); void pci_remove_root_bus(struct pci_bus *bus); -void pci_setup_cardbus(struct pci_bus *bus); +#ifdef CONFIG_CARDBUS +void pci_setup_cardbus_bridge(struct pci_bus *bus); +#else +static inline void pci_setup_cardbus_bridge(struct pci_bus *bus) { } +#endif void pcibios_setup_bridge(struct pci_bus *bus, unsigned long type); void pci_sort_breadthfirst(void); #define dev_is_pci(d) ((d)->bus == &pci_bus_type) diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 3add74ae2594..8be55ece2a21 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -132,6 +132,11 @@ #define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ #define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ #define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ +/* Masks for dword-sized processing of Bus Number and Sec Latency Timer fields */ +#define PCI_PRIMARY_BUS_MASK 0x000000ff +#define PCI_SECONDARY_BUS_MASK 0x0000ff00 +#define PCI_SUBORDINATE_BUS_MASK 0x00ff0000 +#define PCI_SEC_LATENCY_TIMER_MASK 0xff000000 #define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ #define PCI_IO_LIMIT 0x1d #define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */ diff --git a/kernel/resource.c b/kernel/resource.c index e4e9bac12e6e..c5f03ac78e44 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -82,7 +82,7 @@ static struct resource *next_resource(struct resource *p, bool skip_children, #ifdef CONFIG_PROC_FS -enum { MAX_IORES_LEVEL = 5 }; +enum { MAX_IORES_LEVEL = 8 }; static void *r_start(struct seq_file *m, loff_t *pos) __acquires(resource_lock) |
