@@ -1961,6 +1961,18 @@ and is between 256 and 4096 characters. It is defined in the file
PAGE_SIZE is used as alignment.
PCI-PCI bridge can be specified, if resource
windows need to be expanded.
+ override=[off, conflict, always, <device>]
+ off : Do not override BIOS/firmware memory resource
+ allocations. This is the default.
+ conflict : override BIOS/firmware memory resource
+ allocations if conflicting or not allocated.
+ always : override all BIOS/firmware memory resource
+ allocations.
+ <device>: Format [<domain>:]<bus>:<slot>.<func>[; ...]
+ override BIOS/firmware memory resource allocations
+ of specified devices. If the device is a bridge
+ override allocations of all devices under the
+ bridge.
ecrc= Enable/disable PCIe ECRC (transaction layer
end-to-end CRC checking).
bios: Use BIOS/firmware settings. This is the
@@ -18,4 +18,5 @@ extern int early_pci_allowed(void);
extern unsigned int pci_early_dump_regs;
extern void early_dump_pci_device(u8 bus, u8 slot, u8 func);
extern void early_dump_pci_devices(void);
+extern void early_reset_pci_devices(void);
#endif /* _ASM_X86_PCI_DIRECT_H */
@@ -855,6 +855,7 @@ void __init setup_arch(char **cmdline_p)
if (pci_early_dump_regs)
early_dump_pci_devices();
#endif
+ early_reset_pci_devices();
finish_e820_parsing();
@@ -453,6 +453,66 @@ int __init pcibios_init(void)
return 0;
}
+#define RESOURCE_RELEASE_PARAM_SIZE 256
+static char __initdata resource_release_param[RESOURCE_RELEASE_PARAM_SIZE] = {0};
+int pci_override=PCI_OVERRIDE_OFF;
+
+char *next_pci_device(int *bus, int *slot, int *func, char *p)
+{
+ int seg, count;
+
+ if ( !p )
+ p = resource_release_param;
+
+ if ( !*p )
+ return NULL;
+
+ count = 0;
+ if (sscanf(p, "%x:%x:%x.%x%n",
+ &seg, bus, slot, func, &count) != 4) {
+ seg = 0;
+ if (sscanf(p, "%x:%x.%x%n",
+ bus, slot, func, &count) != 3) {
+ /* Invalid format */
+ printk(KERN_ERR "PCI: Can't parse resource_alignment parameter: %s\n",
+ p);
+ return NULL;
+ }
+ }
+ p += count;
+ if (!*p)
+ return p;
+ if (*p == ';')
+ return ++p;
+ return NULL;
+}
+
+
+void pci_override_setup(const char *str, int override)
+{
+ int count;
+ if (override && !strncmp(str, "off", 3)) {
+ pci_override = PCI_OVERRIDE_OFF;
+ printk(KERN_INFO "pci: do not override BIOS/uEFI memory allocations\n");
+ } else if (override && !strncmp(str, "conflict", 8)) {
+ pci_override = PCI_OVERRIDE_CONFLICT;
+ printk(KERN_INFO "pci: reallocate BIOS/uEFI allocated memory resource conflicts\n");
+ } else if (override && !strncmp(str, "always", 6)) {
+ pci_override = PCI_OVERRIDE_ALWAYS;
+ printk(KERN_INFO "pci: override all BIOS/uEFI memory resource allocations\n");
+ } else {
+ pci_override = PCI_OVERRIDE_DEVICE;
+ count=strlen(str);
+ if (count > RESOURCE_RELEASE_PARAM_SIZE - 1)
+ count = RESOURCE_RELEASE_PARAM_SIZE - 1;
+ strncpy(resource_release_param, str, count);
+ resource_release_param[count] = '\0';
+ printk(KERN_INFO "pci: override BIOS/uEFI memory allocations for %s\n",
+ resource_release_param);
+ }
+ return;
+}
+
char * __devinit pcibios_setup(char *str)
{
if (!strcmp(str, "off")) {
@@ -558,6 +618,9 @@ char * __devinit pcibios_setup(char *str)
if (noioapicreroute != -1)
noioapicreroute = 1;
return NULL;
+ } else if (!strncmp(str, "override=", 9)) {
+ pci_override_setup(str + 9, 1);
+ return NULL;
}
return str;
}
@@ -109,3 +109,101 @@ void early_dump_pci_devices(void)
}
}
}
+
+static void __reset_bridge_window(u8 bus, u8 slot, u8 func)
+{
+ /* reset mem resources */
+ write_pci_config_16(bus, slot, func, PCI_MEMORY_BASE, 0x00);
+
+ /* reset pref mem resources */
+ write_pci_config(bus, slot, func, PCI_PREF_LIMIT_UPPER32, 0x00);
+ write_pci_config(bus, slot, func, PCI_PREF_MEMORY_BASE, 0x00);
+ write_pci_config(bus, slot, func, PCI_PREF_BASE_UPPER32, 0x00);
+ write_pci_config(bus, slot, func, PCI_PREF_LIMIT_UPPER32, 0x00);
+}
+
+
+int find_parent_bridge(u8 *bus, u8 *slot, u8 *func, u8 secondary_bus)
+{
+ u8 tbus, tslot, tfunc;
+ for (tbus = secondary_bus-1; tbus >= 0; tbus--) {
+ for (tslot = 0; tslot < 32; tslot++) {
+ for (tfunc = 0; tfunc < 8; tfunc++) {
+ if (0xffffffff == read_pci_config(tbus,
+ tslot, tfunc, PCI_CLASS_REVISION))
+ continue;
+
+ if (read_pci_config_byte(tbus, tslot, tfunc,
+ PCI_HEADER_TYPE) != PCI_HEADER_TYPE_BRIDGE)
+ continue;
+
+ if (secondary_bus == read_pci_config_byte(tbus,
+ tslot, tfunc, PCI_SECONDARY_BUS)) {
+ *bus=tbus;
+ *slot=tslot;
+ *func=tfunc;
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+void reset_bridge_window(u8 bus, u8 slot, u8 func)
+{
+ u8 tbus, tslot, tfunc;
+ if (read_pci_config_byte(bus, slot, func, PCI_HEADER_TYPE) ==
+ PCI_HEADER_TYPE_BRIDGE) {
+ __reset_bridge_window(bus, slot, func);
+ } else if (find_parent_bridge(&tbus, &tslot, &tfunc, bus))
+ __reset_bridge_window(tbus, tslot, tfunc);
+ return;
+}
+
+void reset_topmost_bridges(void)
+{
+ u8 slot, func;
+
+ /* topmost bridges as assumed to be on bus 0. good assumption? */
+ for (slot = 0; slot < 32; slot++) {
+ for (func = 0; func < 8; func++) {
+ if (0xffffffff == read_pci_config(0, slot, func, PCI_CLASS_REVISION))
+ continue;
+
+ if (read_pci_config_byte(0, slot, func, PCI_HEADER_TYPE) ==
+ PCI_HEADER_TYPE_BRIDGE) {
+ __reset_bridge_window(0, slot, func);
+ }
+ }
+ }
+ return;
+}
+
+extern char * next_pci_device(int *bus, int *slot, int *func, char *pr);
+extern int pci_override;
+
+void early_reset_pci_devices(void)
+{
+ unsigned bus, slot, func;
+ char *ptr=NULL;
+
+ if (!early_pci_allowed())
+ return;
+
+ if ( pci_override == PCI_OVERRIDE_OFF )
+ return;
+
+ if ( pci_override == PCI_OVERRIDE_ALWAYS ) {
+ reset_topmost_bridges();
+ return;
+ }
+
+ while((ptr = next_pci_device(&bus, &slot, &func, ptr))) {
+ if (0xffffffff == read_pci_config(bus, slot,
+ func, PCI_CLASS_REVISION))
+ continue;
+ reset_bridge_window(bus, slot, func);
+ }
+ return;
+}
@@ -768,6 +768,7 @@ static void pci_bridge_release_resources(struct pci_bus *bus,
}
}
+
enum release_type {
leaf_only,
whole_subtree,
@@ -808,6 +809,7 @@ static void __ref pci_bus_release_bridge_resources(struct pci_bus *bus,
pci_bridge_release_resources(bus, type);
}
+
static void pci_bus_dump_res(struct pci_bus *bus)
{
struct resource *res;
@@ -838,26 +840,78 @@ static void pci_bus_dump_resources(struct pci_bus *bus)
}
}
+static void __init
+pci_release_resources(struct resource_list_x *head)
+{
+ struct resource_list_x *list;
+ unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
+ IORESOURCE_PREFETCH;
+ enum release_type rel_type = whole_subtree;
+ /*
+ * Try to release leaf bridge's resources that doesn't fit resource of
+ * child device under that bridge
+ */
+ for (list = head->next; list;) {
+ struct pci_bus *bus = list->dev->bus;
+ pci_bus_release_bridge_resources(bus, list->flags & type_mask,
+ rel_type);
+ list = list->next;
+ }
+ /* restore size and flags */
+ for (list = head->next; list;) {
+ struct resource *res = list->res;
+
+ res->start = list->start;
+ res->end = list->end;
+ res->flags = list->flags;
+ if (list->dev->subordinate)
+ res->flags = 0;
+
+ list = list->next;
+ }
+ free_failed_list(head);
+}
+
void __init
pci_assign_unassigned_resources(void)
{
struct pci_bus *bus;
+ int tried_times = 0;
+ struct resource_list_x head;
+
+ head.next = NULL;
+ do { /* loop at most 2 times */
+ /* Depth first, calculate sizes and alignments of all
+ subordinate buses. */
+ list_for_each_entry(bus, &pci_root_buses, node) {
+ pci_bus_size_bridges(bus);
+ }
+ /* Depth last, allocate resources and update the hardware. */
+ list_for_each_entry(bus, &pci_root_buses, node) {
+ __pci_bus_assign_resources(bus, &head);
+ }
+ /* any device complain? */
+ if (!head.next)
+ goto enable_and_dump;
+
+ /* do we care if any device complained? */
+ if (pci_override != PCI_OVERRIDE_CONFLICT) {
+ free_failed_list(&head);
+ goto enable_and_dump;
+ }
+ printk(KERN_INFO "PCI: No. %d try to assign unassigned res\n",
+ tried_times+1);
- /* Depth first, calculate sizes and alignments of all
- subordinate buses. */
- list_for_each_entry(bus, &pci_root_buses, node) {
- pci_bus_size_bridges(bus);
- }
- /* Depth last, allocate resources and update the hardware. */
- list_for_each_entry(bus, &pci_root_buses, node) {
- pci_bus_assign_resources(bus);
+ pci_release_resources(&head);
+ } while (!tried_times++);
+
+enable_and_dump:
+ list_for_each_entry(bus, &pci_root_buses, node)
pci_enable_bridges(bus);
- }
/* dump the resource on buses */
- list_for_each_entry(bus, &pci_root_buses, node) {
+ list_for_each_entry(bus, &pci_root_buses, node)
pci_bus_dump_resources(bus);
- }
}
void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
@@ -1360,6 +1360,12 @@ extern u8 pci_cache_line_size;
extern unsigned long pci_hotplug_io_size;
extern unsigned long pci_hotplug_mem_size;
+extern int pci_override;
+#define PCI_OVERRIDE_OFF 1
+#define PCI_OVERRIDE_CONFLICT 2
+#define PCI_OVERRIDE_ALWAYS 3
+#define PCI_OVERRIDE_DEVICE 4
+
int pcibios_add_platform_entries(struct pci_dev *dev);
void pcibios_disable_device(struct pci_dev *dev);
int pcibios_set_pcie_reset_state(struct pci_dev *dev,