Some may have been following the beginning of this thread on linux-kernel, see for example http://www.opensubscriber.com/message/linux-kernel@xxxxxxxxxxxxxxx/8161812.html for non-subscribers. Linus has reverted the offending patch fd6e732186ab522c812ab19c2c5e5befb8ec8115 in question so now the PCI and I/O port code needs to be fixed up to be able to handle such a setup. A test report of this patch which is meant to be applied on top of today's lmo git kernel on a Cobalt would be appreciated. Ralf [MIPS] Cobalt: Sort out legacy I/O port addressing. Thanks to the brainfucked GT-64111 which passes CPU addresses in the PCI I/O window to the PCI bus without any translation a Cobalt cannot generate a low I/O port address that is below < 0x10000000 in the GT-64111 default configuration. Fortunately the VIA VT82C586 southbridge used in Cobalts only decodes the low 16 bits of the I/O port address for its legacy devices. This can be exploited to generate equivalent addresses but need to use an address different from mips_io_port_base as the base to work. In addition PCI fixups need to be prevented from tinkering with legacy addresses. Signed-off-by: Ralf Baechle <ralf@xxxxxxxxxxxxxx> diff --git a/arch/mips/cobalt/pci.c b/arch/mips/cobalt/pci.c index cfce7af..b1fc6bd 100644 --- a/arch/mips/cobalt/pci.c +++ b/arch/mips/cobalt/pci.c @@ -24,8 +24,8 @@ static struct resource cobalt_mem_resource = { }; static struct resource cobalt_io_resource = { - .start = 0x1000, - .end = GT_DEF_PCI0_IO_SIZE - 1, + .start = GT_DEF_PCI0_IO_BASE + 0x1000, + .end = GT_DEF_PCI0_IO_BASE + GT_DEF_PCI0_IO_SIZE - 1, .name = "PCI I/O", .flags = IORESOURCE_IO, }; @@ -34,7 +34,7 @@ static struct pci_controller cobalt_pci_controller = { .pci_ops = >64xxx_pci0_ops, .mem_resource = &cobalt_mem_resource, .io_resource = &cobalt_io_resource, - .io_offset = 0 - GT_DEF_PCI0_IO_BASE, + .io_offset = 0, .io_map_base = CKSEG1ADDR(GT_DEF_PCI0_IO_BASE), }; diff --git a/arch/mips/cobalt/setup.c b/arch/mips/cobalt/setup.c index dd23beb..f99eaa9 100644 --- a/arch/mips/cobalt/setup.c +++ b/arch/mips/cobalt/setup.c @@ -79,10 +79,11 @@ void __init plat_mem_setup(void) _machine_halt = cobalt_machine_halt; pm_power_off = cobalt_machine_halt; - set_io_port_base(CKSEG1ADDR(GT_DEF_PCI0_IO_BASE)); + set_io_port_base(IO_BASE); + set_legacy_io_port_base(CKSEG1ADDR(GT_DEF_PCI0_IO_BASE)); /* I/O port resource must include LCD/buttons */ - ioport_resource.end = 0x0fffffff; + ioport_resource.end = GT_DEF_PCI0_IO_BASE + GT_DEF_PCI0_IO_SIZE - 1; /* These resources have been reserved by VIA SuperI/O chip. */ for (i = 0; i < ARRAY_SIZE(cobalt_reserved_resources); i++) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 7f6ddcb..f8b3f5a 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -60,11 +60,14 @@ static char command_line[CL_SIZE]; char arcs_cmdline[CL_SIZE]=CONFIG_CMDLINE; /* - * mips_io_port_base is the begin of the address space to which x86 style - * I/O ports are mapped. + * mips_io_port_base is the base of the address range into which the I/O ports + * are mapped. mips_legacy_io_port_base is the same but used only for legacy + * addresses below legacy_io_port_top. */ const unsigned long mips_io_port_base __read_mostly = -1; EXPORT_SYMBOL(mips_io_port_base); +const unsigned long mips_legacy_io_port_base __read_mostly = -1; +EXPORT_SYMBOL(mips_legacy_io_port_base); /* * isa_slot_offset is the address where E(ISA) busaddress 0 is mapped diff --git a/arch/mips/pci/fixup-cobalt.c b/arch/mips/pci/fixup-cobalt.c index f7df114..7da133b 100644 --- a/arch/mips/pci/fixup-cobalt.c +++ b/arch/mips/pci/fixup-cobalt.c @@ -51,6 +51,18 @@ static void qube_raq_galileo_early_fixup(struct pci_dev *dev) DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_GT64111, qube_raq_galileo_early_fixup); +static void __devinit cobalt_via_ide_native_mode_fixup(struct pci_dev *dev) +{ + unsigned char pif; + + pci_read_config_byte(dev, PCI_CLASS_PROG, &pif); + pif |= 4; + pci_write_config_byte(dev, PCI_CLASS_PROG, pif); +} + +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, + cobalt_via_ide_native_mode_fixup); + static void qube_raq_via_bmIDE_fixup(struct pci_dev *dev) { unsigned short cfgword; diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index 589b745..6e6981f 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -242,6 +242,8 @@ static void pcibios_fixup_device_resources(struct pci_dev *dev, for (i = 0; i < PCI_NUM_RESOURCES; i++) { if (!dev->resource[i].start) continue; + if (dev->resource[i].flags & IORESOURCE_PCI_FIXED) + continue; if (dev->resource[i].flags & IORESOURCE_IO) offset = hose->io_offset; else if (dev->resource[i].flags & IORESOURCE_MEM) diff --git a/include/asm-mips/io.h b/include/asm-mips/io.h index e62058b..953be3b 100644 --- a/include/asm-mips/io.h +++ b/include/asm-mips/io.h @@ -53,11 +53,16 @@ /* * On MIPS I/O ports are memory mapped, so we access them using normal * load/store instructions. mips_io_port_base is the virtual address to - * which all ports are being mapped. For sake of efficiency some code - * assumes that this is an address that can be loaded with a single lui - * instruction, so the lower 16 bits must be zero. Should be true on - * on any sane architecture; generic code does not use this assumption. + * which all ports are being mapped. mips_legacy_io_port_base the same + * but used for ports below legacy_io_port_top. This special treatment + * of legacy addresses is used for example on Cobalt systems where the + * GT-64111 system controller doesn't allow access to low ports but aliasing + * can be exploited to access them anyway. So on a GT-64111 system + * mips_legacy_io_port_base would point to the base of the address range + * to which I/O ports are aliased. Having the legacy_io_port_top default of + * zero leaves this special treatment disabled by default. */ +extern const unsigned long mips_legacy_io_port_base; extern const unsigned long mips_io_port_base; /* @@ -75,6 +80,12 @@ static inline void set_io_port_base(unsigned long base) barrier(); } +static inline void set_legacy_io_port_base(unsigned long base) +{ + * (unsigned long *) &mips_legacy_io_port_base = base; + barrier(); +} + /* * Thanks to James van Artsdalen for a better timing-fix than * the two short jumps: using outb's to a nonexistent port seems @@ -375,9 +386,14 @@ static inline type pfx##read##bwlq(const volatile void __iomem *mem) \ static inline void pfx##out##bwlq##p(type val, unsigned long port) \ { \ volatile type *__addr; \ + unsigned long __vaddr; \ type __val; \ \ - __addr = (void *)__swizzle_addr_##bwlq(mips_io_port_base + port); \ + if (port < legacy_io_port_top) \ + __vaddr = mips_legacy_io_port_base + port; \ + else \ + __vaddr = mips_io_port_base + port; \ + __addr = (void *)__swizzle_addr_##bwlq(__vaddr); \ \ __val = pfx##ioswab##bwlq(__addr, val); \ \ @@ -391,9 +407,14 @@ static inline void pfx##out##bwlq##p(type val, unsigned long port) \ static inline type pfx##in##bwlq##p(unsigned long port) \ { \ volatile type *__addr; \ + unsigned long __vaddr; \ type __val; \ \ - __addr = (void *)__swizzle_addr_##bwlq(mips_io_port_base + port); \ + if (port < legacy_io_port_top) \ + __vaddr = mips_legacy_io_port_base + port; \ + else \ + __vaddr = mips_io_port_base + port; \ + __addr = (void *)__swizzle_addr_##bwlq(__vaddr); \ \ BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long)); \ \ diff --git a/include/asm-mips/mach-cobalt/mangle-port.h b/include/asm-mips/mach-cobalt/mangle-port.h new file mode 100644 index 0000000..bdf5450 --- /dev/null +++ b/include/asm-mips/mach-cobalt/mangle-port.h @@ -0,0 +1,27 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2003, 2004, 2007 Ralf Baechle (ralf@xxxxxxxxxxxxxx) + */ +#ifndef __ASM_MACH_COBALT_MANGLE_PORT_H +#define __ASM_MACH_COBALT_MANGLE_PORT_H + +#define __swizzle_addr_b(port) (port) +#define __swizzle_addr_w(port) (port) +#define __swizzle_addr_l(port) (port) +#define __swizzle_addr_q(port) (port) + +# define ioswabb(a, x) (x) +# define __mem_ioswabb(a, x) (x) +# define ioswabw(a, x) (x) +# define __mem_ioswabw(a, x) cpu_to_le16(x) +# define ioswabl(a, x) (x) +# define __mem_ioswabl(a, x) cpu_to_le32(x) +# define ioswabq(a, x) (x) +# define __mem_ioswabq(a, x) cpu_to_le32(x) + +#define legacy_io_port_top 0 + +#endif /* __ASM_MACH_COBALT_MANGLE_PORT_H */ diff --git a/include/asm-mips/mach-generic/mangle-port.h b/include/asm-mips/mach-generic/mangle-port.h index f49dc99..7f59bff 100644 --- a/include/asm-mips/mach-generic/mangle-port.h +++ b/include/asm-mips/mach-generic/mangle-port.h @@ -49,4 +49,6 @@ #endif +#define legacy_io_port_top 0 + #endif /* __ASM_MACH_GENERIC_MANGLE_PORT_H */