diff options
author | Paul Mackerras <paulus@samba.org> | 2007-05-07 01:16:23 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-05-07 21:54:19 -0400 |
commit | 11fbb00c67e19737757e747ec7dd3ba8d584f5d1 (patch) | |
tree | 72490560f34432c23abb3afc9b380f39af2c5047 /arch | |
parent | 0bd15c4b503b971024a3962b6a6b34c1af0628bf (diff) |
[POWERPC] Cope with PCI host bridge I/O window not starting at 0
Currently our code to set up the data structures for a PCI host bridge
and create the mapping for its I/O window assumes that the window
starts at I/O port 0 on the PCI side. If this is not true, we can end
up with I/O port numbers in the resources for PCI devices which will
cause an oops if a driver tries to access them via inb/outb etc.,
because there is no mapping for the corresponding addresses.
Normally the I/O window starts at 0, but there are some situations on
partitioned machines with a hypervisor where the window may not start
at 0.
This fixes the problem by allocating space for the range from 0 to the
end of the I/O window. That is, hose->io_base_virt contains the
virtual address for I/O port 0 on the PCI bus, and thus the assumption
that hose->io_base_virt - pci_io_base is the offset between the
"global" I/O port numbers (those in the PCI device resources) and the
I/O port numbers on the PCI bus is maintained.
For PCI host bridges that are present at boot, we only map the portion
of that range that correspond to the bridge's I/O window. For bridges
added after boot we ioremap the range from 0 to the end of the I/O
window, for now; in fact hot-added bridges should be using
reserve_phb_iospace() and __ioremap_explicit (so they get sensible
global port numbers), but we don't have the infrastructure yet to do
that (basically a free_phb_iospace() routine plus appropriate
locking).
Interestingly, this makes the two arms of the if statement in
get_bus_io_range do almost exactly the same thing; that function could
now be simplified in a further patch.
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/kernel/pci_64.c | 9 |
1 files changed, 5 insertions, 4 deletions
diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index 60d7d4baa227..706b7f3da5ff 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c | |||
@@ -1006,8 +1006,9 @@ void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose, | |||
1006 | 1006 | ||
1007 | switch ((pci_space >> 24) & 0x3) { | 1007 | switch ((pci_space >> 24) & 0x3) { |
1008 | case 1: /* I/O space */ | 1008 | case 1: /* I/O space */ |
1009 | hose->io_base_phys = cpu_phys_addr; | 1009 | hose->io_base_phys = cpu_phys_addr - pci_addr; |
1010 | hose->pci_io_size = size; | 1010 | /* handle from 0 to top of I/O window */ |
1011 | hose->pci_io_size = pci_addr + size; | ||
1011 | 1012 | ||
1012 | res = &hose->io_resource; | 1013 | res = &hose->io_resource; |
1013 | res->flags = IORESOURCE_IO; | 1014 | res->flags = IORESOURCE_IO; |
@@ -1117,8 +1118,8 @@ static int get_bus_io_range(struct pci_bus *bus, unsigned long *start_phys, | |||
1117 | } else { | 1118 | } else { |
1118 | /* Root Bus */ | 1119 | /* Root Bus */ |
1119 | res = &hose->io_resource; | 1120 | res = &hose->io_resource; |
1120 | *start_phys = hose->io_base_phys; | 1121 | *start_phys = hose->io_base_phys + res->start; |
1121 | *start_virt = (unsigned long) hose->io_base_virt; | 1122 | *start_virt = (unsigned long) hose->io_base_virt + res->start; |
1122 | if (res->end > res->start) | 1123 | if (res->end > res->start) |
1123 | *size = res->end - res->start + 1; | 1124 | *size = res->end - res->start + 1; |
1124 | else { | 1125 | else { |