Subject: [RFC PATCH] PCI: io port 16/32 bits allocation pci bridge could only support 16bit io port. some arches are supporting 32bit io ports. So need to make sure 16bit ioport will be allocated under 64k. For 32bit ioport, will try to allocate that [64k, 4g), and if it fail will fall back to [0, 64k). It is some kind of clone of 64bit mem io handling. Signed-off-by: Yinghai Lu --- drivers/pci/bus.c | 20 +++++++++++++++----- drivers/pci/pci.h | 1 + drivers/pci/probe.c | 4 +++- drivers/pci/setup-bus.c | 11 +++++++++-- include/linux/ioport.h | 1 + 5 files changed, 29 insertions(+), 8 deletions(-) Index: linux-2.6/drivers/pci/bus.c =================================================================== --- linux-2.6.orig/drivers/pci/bus.c +++ linux-2.6/drivers/pci/bus.c @@ -130,12 +130,22 @@ pci_bus_alloc_resource(struct pci_bus *b * don't allocate too high if the pref mem doesn't support 64bit * also if this is a 64-bit mem resource, try above 4GB first */ - if (res->flags & IORESOURCE_MEM_64) { - start = (resource_size_t)(1ULL<<32); - end = PCI_MAX_RESOURCE; + if (res->flags & IORESOURCE_MEM) { + if (res->flags & IORESOURCE_MEM_64) { + start = (resource_size_t)(1ULL<<32); + end = PCI_MAX_RESOURCE; + } else { + start = 0; + end = PCI_MAX_RESOURCE_32; + } } else { - start = 0; - end = PCI_MAX_RESOURCE_32; + if (res->flags & IORESOURCE_IO_32) { + start = PCI_MAX_RESOURCE_16 + 1; + end = PCI_MAX_RESOURCE_32; + } else { + start = 0; + end = PCI_MAX_RESOURCE_16; + } } again: Index: linux-2.6/drivers/pci/pci.h =================================================================== --- linux-2.6.orig/drivers/pci/pci.h +++ linux-2.6/drivers/pci/pci.h @@ -210,6 +210,7 @@ enum pci_bar_type { #define PCI_MAX_RESOURCE ((resource_size_t)~0) #define PCI_MAX_RESOURCE_32 ((resource_size_t)0xffffffff) +#define PCI_MAX_RESOURCE_16 ((resource_size_t)0xffff) bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl, int crs_timeout); Index: linux-2.6/include/linux/ioport.h =================================================================== --- linux-2.6.orig/include/linux/ioport.h +++ linux-2.6/include/linux/ioport.h @@ -47,6 +47,7 @@ struct resource { #define IORESOURCE_MEM_64 0x00100000 #define IORESOURCE_WINDOW 0x00200000 /* forwarded by bridge */ #define IORESOURCE_MUXED 0x00400000 /* Resource is software muxed */ +#define IORESOURCE_IO_32 0x00800000 #define IORESOURCE_EXCLUSIVE 0x08000000 /* Userland may not map this resource */ #define IORESOURCE_DISABLED 0x10000000 Index: linux-2.6/drivers/pci/probe.c =================================================================== --- linux-2.6.orig/drivers/pci/probe.c +++ linux-2.6/drivers/pci/probe.c @@ -130,7 +130,7 @@ static inline unsigned long decode_bar(s if ((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { flags = bar & ~PCI_BASE_ADDRESS_IO_MASK; - flags |= IORESOURCE_IO; + flags |= IORESOURCE_IO | IORESOURCE_IO_32; return flags; } @@ -326,6 +326,8 @@ static void __devinit pci_read_bridge_io if (base && base <= limit) { res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; + if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) + res->flags |= IORESOURCE_IO_32; res2.flags = res->flags; region.start = base; region.end = limit + 0xfff; Index: linux-2.6/drivers/pci/setup-bus.c =================================================================== --- linux-2.6.orig/drivers/pci/setup-bus.c +++ linux-2.6/drivers/pci/setup-bus.c @@ -595,8 +595,11 @@ static void pci_bridge_check_ranges(stru pci_read_config_word(bridge, PCI_IO_BASE, &io); pci_write_config_word(bridge, PCI_IO_BASE, 0x0); } - if (io) + if (io) { b_res[0].flags |= IORESOURCE_IO; + if ((io & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) + b_res[0].flags |= IORESOURCE_IO_32; + } /* DECchip 21050 pass 2 errata: the bridge may miss an address disconnect boundary by one PCI data phase. Workaround: do not use prefetching on this device. */ @@ -710,10 +713,13 @@ static void pbus_size_io(struct pci_bus struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO); unsigned long size = 0, size0 = 0, size1 = 0; resource_size_t children_add_size = 0; + unsigned int io32_mask = 0; if (!b_res) return; + io32_mask = b_res->flags & IORESOURCE_IO_32; + b_res->flags &= ~IORESOURCE_IO_32; list_for_each_entry(dev, &bus->devices, bus_list) { int i; @@ -731,6 +737,7 @@ static void pbus_size_io(struct pci_bus else size1 += r_size; + io32_mask &= r->flags & IORESOURCE_IO_32; if (realloc_head) children_add_size += get_res_add_size(realloc_head, r); } @@ -753,7 +760,7 @@ static void pbus_size_io(struct pci_bus /* Alignment of the IO window is always 4K */ b_res->start = 4096; b_res->end = b_res->start + size0 - 1; - b_res->flags |= IORESOURCE_STARTALIGN; + b_res->flags |= IORESOURCE_STARTALIGN | io32_mask; if (size1 > size0 && realloc_head) { add_to_list(realloc_head, bus->self, b_res, size1-size0, 4096); dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window "