#include <linux/ioport.h>
#include <linux/cache.h>
#include <linux/slab.h>
+#include "pci.h"
-
-#define DEBUG_CONFIG 1
-#if DEBUG_CONFIG
-#define DBG(x...) printk(x)
-#else
-#define DBG(x...)
-#endif
-
-static void pbus_assign_resources_sorted(struct pci_bus *bus)
+static void pbus_assign_resources_sorted(const struct pci_bus *bus)
{
struct pci_dev *dev;
struct resource *res;
struct pci_dev *bridge = bus->self;
struct pci_bus_region region;
- printk("PCI: Bus %d, cardbus bridge: %s\n",
- bus->number, pci_name(bridge));
+ dev_info(&bridge->dev, "CardBus bridge, secondary bus %04x:%02x\n",
+ pci_domain_nr(bus), bus->number);
pcibios_resource_to_bus(bridge, ®ion, bus->resource[0]);
if (bus->resource[0]->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.
*/
- printk(KERN_INFO " IO window: 0x%08lx-0x%08lx\n",
+ dev_info(&bridge->dev, " IO window: %#08lx-%#08lx\n",
(unsigned long)region.start,
(unsigned long)region.end);
pci_write_config_dword(bridge, PCI_CB_IO_BASE_0,
pcibios_resource_to_bus(bridge, ®ion, bus->resource[1]);
if (bus->resource[1]->flags & IORESOURCE_IO) {
- printk(KERN_INFO " IO window: 0x%08lx-0x%08lx\n",
+ dev_info(&bridge->dev, " IO window: %#08lx-%#08lx\n",
(unsigned long)region.start,
(unsigned long)region.end);
pci_write_config_dword(bridge, PCI_CB_IO_BASE_1,
pcibios_resource_to_bus(bridge, ®ion, bus->resource[2]);
if (bus->resource[2]->flags & IORESOURCE_MEM) {
- printk(KERN_INFO " PREFETCH window: 0x%08lx-0x%08lx\n",
+ dev_info(&bridge->dev, " PREFETCH window: %#08lx-%#08lx\n",
(unsigned long)region.start,
(unsigned long)region.end);
pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0,
pcibios_resource_to_bus(bridge, ®ion, bus->resource[3]);
if (bus->resource[3]->flags & IORESOURCE_MEM) {
- printk(KERN_INFO " MEM window: 0x%08lx-0x%08lx\n",
+ dev_info(&bridge->dev, " MEM window: %#08lx-%#08lx\n",
(unsigned long)region.start,
(unsigned long)region.end);
pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1,
config space writes, so it's quite possible that an I/O window of
the bridge will have some undesirable address (e.g. 0) after the
first write. Ditto 64-bit prefetchable MMIO. */
-static void __devinit
-pci_setup_bridge(struct pci_bus *bus)
+static void pci_setup_bridge(struct pci_bus *bus)
{
struct pci_dev *bridge = bus->self;
struct pci_bus_region region;
u32 l, bu, lu, io_upper16;
+ int pref_mem64;
+
+ if (pci_is_enabled(bridge))
+ return;
- DBG(KERN_INFO "PCI: Bridge: %s\n", pci_name(bridge));
+ dev_info(&bridge->dev, "PCI bridge, secondary bus %04x:%02x\n",
+ pci_domain_nr(bus), bus->number);
/* Set up the top and bottom of the PCI I/O segment for this bus. */
pcibios_resource_to_bus(bridge, ®ion, bus->resource[0]);
l |= region.end & 0xf000;
/* Set up upper 16 bits of I/O base/limit. */
io_upper16 = (region.end & 0xffff0000) | (region.start >> 16);
- DBG(KERN_INFO " IO window: %04lx-%04lx\n",
+ dev_info(&bridge->dev, " IO window: %#04lx-%#04lx\n",
(unsigned long)region.start,
(unsigned long)region.end);
}
/* Clear upper 16 bits of I/O base/limit. */
io_upper16 = 0;
l = 0x00f0;
- DBG(KERN_INFO " IO window: disabled.\n");
+ dev_info(&bridge->dev, " IO window: disabled\n");
}
/* Temporarily disable the I/O range before updating PCI_IO_BASE. */
pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff);
if (bus->resource[1]->flags & IORESOURCE_MEM) {
l = (region.start >> 16) & 0xfff0;
l |= region.end & 0xfff00000;
- DBG(KERN_INFO " MEM window: 0x%08lx-0x%08lx\n",
+ dev_info(&bridge->dev, " MEM window: %#08lx-%#08lx\n",
(unsigned long)region.start,
(unsigned long)region.end);
}
else {
l = 0x0000fff0;
- DBG(KERN_INFO " MEM window: disabled.\n");
+ dev_info(&bridge->dev, " MEM window: disabled\n");
}
pci_write_config_dword(bridge, PCI_MEMORY_BASE, l);
pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0);
/* Set up PREF base/limit. */
+ pref_mem64 = 0;
bu = lu = 0;
pcibios_resource_to_bus(bridge, ®ion, bus->resource[2]);
if (bus->resource[2]->flags & IORESOURCE_PREFETCH) {
+ int width = 8;
l = (region.start >> 16) & 0xfff0;
l |= region.end & 0xfff00000;
- bu = upper_32_bits(region.start);
- lu = upper_32_bits(region.end);
- DBG(KERN_INFO " PREFETCH window: 0x%016llx-0x%016llx\n",
- (unsigned long long)region.start,
- (unsigned long long)region.end);
+ if (bus->resource[2]->flags & IORESOURCE_MEM_64) {
+ pref_mem64 = 1;
+ bu = upper_32_bits(region.start);
+ lu = upper_32_bits(region.end);
+ width = 16;
+ }
+ dev_info(&bridge->dev, " PREFETCH window: %#0*llx-%#0*llx\n",
+ width, (unsigned long long)region.start,
+ width, (unsigned long long)region.end);
}
else {
l = 0x0000fff0;
- DBG(KERN_INFO " PREFETCH window: disabled.\n");
+ dev_info(&bridge->dev, " PREFETCH window: disabled\n");
}
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l);
- /* Set the upper 32 bits of PREF base & limit. */
- pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu);
- pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu);
+ if (pref_mem64) {
+ /* Set the upper 32 bits of PREF base & limit. */
+ pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu);
+ pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu);
+ }
pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl);
}
pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0);
}
- if (pmem)
+ if (pmem) {
b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
+ if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64)
+ b_res[2].flags |= IORESOURCE_MEM_64;
+ }
+
+ /* double check if bridge does support 64 bit pref */
+ if (b_res[2].flags & IORESOURCE_MEM_64) {
+ u32 mem_base_hi, tmp;
+ pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32,
+ &mem_base_hi);
+ pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32,
+ 0xffffffff);
+ pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp);
+ if (!tmp)
+ b_res[2].flags &= ~IORESOURCE_MEM_64;
+ pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32,
+ mem_base_hi);
+ }
}
/* Helper function for sizing routines: find first available
if (r->parent || !(r->flags & IORESOURCE_IO))
continue;
- r_size = r->end - r->start + 1;
+ r_size = resource_size(r);
if (r_size < 0x400)
/* Might be re-aligned for ISA */
/* Alignment of the IO window is always 4K */
b_res->start = 4096;
b_res->end = b_res->start + size - 1;
+ b_res->flags |= IORESOURCE_STARTALIGN;
}
/* Calculate the size of the bus and minimal alignment which
resource_size_t aligns[12]; /* Alignments from 1Mb to 2Gb */
int order, max_order;
struct resource *b_res = find_free_bus_resource(bus, type);
+ unsigned int mem64_mask = 0;
if (!b_res)
return 0;
max_order = 0;
size = 0;
+ mem64_mask = b_res->flags & IORESOURCE_MEM_64;
+ b_res->flags &= ~IORESOURCE_MEM_64;
+
list_for_each_entry(dev, &bus->devices, bus_list) {
int i;
-
+
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *r = &dev->resource[i];
resource_size_t r_size;
if (r->parent || (r->flags & mask) != type)
continue;
- r_size = r->end - r->start + 1;
+ r_size = resource_size(r);
/* For bridges size != alignment */
- align = (i < PCI_BRIDGE_RESOURCES) ? r_size : r->start;
+ align = pci_resource_alignment(dev, r);
order = __ffs(align) - 20;
if (order > 11) {
- printk(KERN_WARNING "PCI: region %s/%d "
- "too large: 0x%016llx-0x%016llx\n",
- pci_name(dev), i,
- (unsigned long long)r->start,
- (unsigned long long)r->end);
+ dev_warn(&dev->dev, "BAR %d bad alignment %llx: "
+ "%pR\n", i, (unsigned long long)align, r);
r->flags = 0;
continue;
}
aligns[order] += align;
if (order > max_order)
max_order = order;
+ mem64_mask &= r->flags & IORESOURCE_MEM_64;
}
}
align = 0;
min_align = 0;
for (order = 0; order <= max_order; order++) {
-#ifdef CONFIG_RESOURCES_64BIT
- resource_size_t align1 = 1ULL << (order + 20);
-#else
- resource_size_t align1 = 1U << (order + 20);
-#endif
+ resource_size_t align1 = 1;
+
+ align1 <<= (order + 20);
+
if (!align)
min_align = align1;
else if (ALIGN(align + min_align, min_align) < align1)
}
b_res->start = min_align;
b_res->end = size + min_align - 1;
+ b_res->flags |= IORESOURCE_STARTALIGN;
+ b_res->flags |= mem64_mask;
return 1;
}
-static void __devinit
-pci_bus_size_cardbus(struct pci_bus *bus)
+static void pci_bus_size_cardbus(struct pci_bus *bus)
{
struct pci_dev *bridge = bus->self;
struct resource *b_res = &bridge->resource[PCI_BRIDGE_RESOURCES];
* Reserve some resources for CardBus. We reserve
* a fixed amount of bus space for CardBus bridges.
*/
- b_res[0].start = pci_cardbus_io_size;
- b_res[0].end = b_res[0].start + pci_cardbus_io_size - 1;
- b_res[0].flags |= IORESOURCE_IO;
+ b_res[0].start = 0;
+ b_res[0].end = pci_cardbus_io_size - 1;
+ b_res[0].flags |= IORESOURCE_IO | IORESOURCE_SIZEALIGN;
- b_res[1].start = pci_cardbus_io_size;
- b_res[1].end = b_res[1].start + pci_cardbus_io_size - 1;
- b_res[1].flags |= IORESOURCE_IO;
+ b_res[1].start = 0;
+ b_res[1].end = pci_cardbus_io_size - 1;
+ b_res[1].flags |= IORESOURCE_IO | IORESOURCE_SIZEALIGN;
/*
* Check whether prefetchable memory is supported
* twice the size.
*/
if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) {
- b_res[2].start = pci_cardbus_mem_size;
- b_res[2].end = b_res[2].start + pci_cardbus_mem_size - 1;
- b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
+ b_res[2].start = 0;
+ b_res[2].end = pci_cardbus_mem_size - 1;
+ b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_SIZEALIGN;
- b_res[3].start = pci_cardbus_mem_size;
- b_res[3].end = b_res[3].start + pci_cardbus_mem_size - 1;
- b_res[3].flags |= IORESOURCE_MEM;
+ b_res[3].start = 0;
+ b_res[3].end = pci_cardbus_mem_size - 1;
+ b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_SIZEALIGN;
} else {
- b_res[3].start = pci_cardbus_mem_size * 2;
- b_res[3].end = b_res[3].start + pci_cardbus_mem_size * 2 - 1;
- b_res[3].flags |= IORESOURCE_MEM;
+ b_res[3].start = 0;
+ b_res[3].end = pci_cardbus_mem_size * 2 - 1;
+ b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_SIZEALIGN;
}
}
break;
case PCI_CLASS_BRIDGE_PCI:
- /* don't size subtractive decoding (transparent)
- * PCI-to-PCI bridges */
- if (bus->self->transparent)
- break;
pci_bridge_check_ranges(bus);
- /* fall through */
default:
pbus_size_io(bus);
/* If the bridge supports prefetchable range, size it
}
EXPORT_SYMBOL(pci_bus_size_bridges);
-void __ref pci_bus_assign_resources(struct pci_bus *bus)
+void __ref pci_bus_assign_resources(const struct pci_bus *bus)
{
struct pci_bus *b;
struct pci_dev *dev;
break;
default:
- printk(KERN_INFO "PCI: not setting up bridge %s "
- "for bus %d\n", pci_name(dev), b->number);
+ dev_info(&dev->dev, "not setting up bridge for bus "
+ "%04x:%02x\n", pci_domain_nr(b), b->number);
break;
}
}
}
EXPORT_SYMBOL(pci_bus_assign_resources);
+static void pci_bus_dump_res(struct pci_bus *bus)
+{
+ int i;
+
+ for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
+ struct resource *res = bus->resource[i];
+ if (!res || !res->end)
+ continue;
+
+ dev_printk(KERN_DEBUG, &bus->dev, "resource %d %s %pR\n", i,
+ (res->flags & IORESOURCE_IO) ? "io: " :
+ ((res->flags & IORESOURCE_PREFETCH)? "pref mem":"mem:"),
+ res);
+ }
+}
+
+static void pci_bus_dump_resources(struct pci_bus *bus)
+{
+ struct pci_bus *b;
+ struct pci_dev *dev;
+
+
+ pci_bus_dump_res(bus);
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ b = dev->subordinate;
+ if (!b)
+ continue;
+
+ pci_bus_dump_resources(b);
+ }
+}
+
void __init
pci_assign_unassigned_resources(void)
{
pci_bus_assign_resources(bus);
pci_enable_bridges(bus);
}
+
+ /* dump the resource on buses */
+ list_for_each_entry(bus, &pci_root_buses, node) {
+ pci_bus_dump_resources(bus);
+ }
}