diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 880f882160e2..9e6056058425 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -794,6 +794,14 @@ and is between 256 and 4096 characters. It is defined in the file for translation below 32 bit and if not available then look in the higher range. + io_delay= [X86-32,X86-64] I/O delay method + standard + Standard port 0x80 delay + alternate + Alternate port 0xed delay + udelay + Simple two microsecond delay + io7= [HW] IO7 for Marvel based alpha systems See comment before marvel_specify_io7 in arch/alpha/kernel/core_marvel.c. diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 761ca7b5f120..40aba670fb37 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -112,4 +112,13 @@ config IOMMU_LEAK Add a simple leak tracer to the IOMMU code. This is useful when you are debugging a buggy device driver that leaks IOMMU mappings. +config UDELAY_IO_DELAY + bool "Delay I/O through udelay instead of outb" + depends on DEBUG_KERNEL + help + Make inb_p/outb_p use udelay() based delays by default. Please note + that udelay() does not have the same bus-level side-effects that + the normal outb based delay does meaning this could cause drivers + to change behaviour and/or bugs to surface. + endmenu diff --git a/arch/x86/boot/compressed/misc_32.c b/arch/x86/boot/compressed/misc_32.c index b74d60d1b2fa..288e16283ef9 100644 --- a/arch/x86/boot/compressed/misc_32.c +++ b/arch/x86/boot/compressed/misc_32.c @@ -276,10 +276,10 @@ static void putstr(const char *s) RM_SCREEN_INFO.orig_y = y; pos = (x + cols * y) * 2; /* Update cursor position */ - outb_p(14, vidport); - outb_p(0xff & (pos >> 9), vidport+1); - outb_p(15, vidport); - outb_p(0xff & (pos >> 1), vidport+1); + outb(14, vidport); + outb(0xff & (pos >> 9), vidport+1); + outb(15, vidport); + outb(0xff & (pos >> 1), vidport+1); } static void* memset(void* s, int c, unsigned n) diff --git a/arch/x86/boot/compressed/misc_64.c b/arch/x86/boot/compressed/misc_64.c index 6ea015aa65e4..43e5fcc37be9 100644 --- a/arch/x86/boot/compressed/misc_64.c +++ b/arch/x86/boot/compressed/misc_64.c @@ -269,10 +269,10 @@ static void putstr(const char *s) RM_SCREEN_INFO.orig_y = y; pos = (x + cols * y) * 2; /* Update cursor position */ - outb_p(14, vidport); - outb_p(0xff & (pos >> 9), vidport+1); - outb_p(15, vidport); - outb_p(0xff & (pos >> 1), vidport+1); + outb(14, vidport); + outb(0xff & (pos >> 9), vidport+1); + outb(15, vidport); + outb(0xff & (pos >> 1), vidport+1); } static void* memset(void* s, int c, unsigned n) diff --git a/arch/x86/kernel/Makefile_32 b/arch/x86/kernel/Makefile_32 index a7bc93c27662..0cc1981d1e38 100644 --- a/arch/x86/kernel/Makefile_32 +++ b/arch/x86/kernel/Makefile_32 @@ -8,7 +8,7 @@ CPPFLAGS_vmlinux.lds += -Ui386 obj-y := process_32.o signal_32.o entry_32.o traps_32.o irq_32.o \ ptrace_32.o time_32.o ioport_32.o ldt_32.o setup_32.o i8259_32.o sys_i386_32.o \ pci-dma_32.o i386_ksyms_32.o i387_32.o bootflag.o e820_32.o\ - quirks.o i8237.o topology.o alternative.o i8253.o tsc_32.o + quirks.o i8237.o topology.o alternative.o i8253.o tsc_32.o io_delay.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += cpu/ diff --git a/arch/x86/kernel/Makefile_64 b/arch/x86/kernel/Makefile_64 index 5a88890d8ee9..08a68f0d8fda 100644 --- a/arch/x86/kernel/Makefile_64 +++ b/arch/x86/kernel/Makefile_64 @@ -11,7 +11,7 @@ obj-y := process_64.o signal_64.o entry_64.o traps_64.o irq_64.o \ x8664_ksyms_64.o i387_64.o syscall_64.o vsyscall_64.o \ setup64.o bootflag.o e820_64.o reboot_64.o quirks.o i8237.o \ pci-dma_64.o pci-nommu_64.o alternative.o hpet.o tsc_64.o bugs_64.o \ - i8253.o + i8253.o io_delay.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += cpu/ diff --git a/arch/x86/kernel/io_delay.c b/arch/x86/kernel/io_delay.c new file mode 100644 index 000000000000..4d955e74b974 --- /dev/null +++ b/arch/x86/kernel/io_delay.c @@ -0,0 +1,106 @@ +/* + * I/O delay strategies for inb_p/outb_p + */ +#include +#include +#include +#include +#include +#include + +/* + * Allow for a DMI based override of port 0x80 needed for certain HP laptops + */ +#define IO_DELAY_PORT_STD 0x80 +#define IO_DELAY_PORT_ALT 0xed + +static void standard_io_delay(void) +{ + asm volatile ("outb %%al, %0" : : "N" (IO_DELAY_PORT_STD)); +} + +static void alternate_io_delay(void) +{ + asm volatile ("outb %%al, %0" : : "N" (IO_DELAY_PORT_ALT)); +} + +/* + * 2 usecs is an upper-bound for the outb delay but note that udelay doesn't + * have the bus-level side-effects that outb does + */ +#define IO_DELAY_USECS 2 + +/* + * High on a hill was a lonely goatherd + */ +static void udelay_io_delay(void) +{ + udelay(IO_DELAY_USECS); +} + +#ifndef CONFIG_UDELAY_IO_DELAY +static void (*io_delay)(void) = standard_io_delay; +#else +static void (*io_delay)(void) = udelay_io_delay; +#endif + +/* + * Paravirt wants native_io_delay to be a constant. + */ +void native_io_delay(void) +{ + io_delay(); +} +EXPORT_SYMBOL(native_io_delay); + +#ifndef CONFIG_UDELAY_IO_DELAY +static int __init dmi_alternate_io_delay_port(const struct dmi_system_id *id) +{ + printk(KERN_NOTICE "%s: using alternate I/O delay port\n", id->ident); + io_delay = alternate_io_delay; + return 0; +} + +static struct dmi_system_id __initdata alternate_io_delay_port_dmi_table[] = { + { + .callback = dmi_alternate_io_delay_port, + .ident = "HP Pavilion dv9000z", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Quanta"), + DMI_MATCH(DMI_BOARD_NAME, "30B9") + } + }, + { + } +}; + +static int __initdata io_delay_override; + +void __init io_delay_init(void) +{ + if (!io_delay_override) + dmi_check_system(alternate_io_delay_port_dmi_table); +} +#endif + +static int __init io_delay_param(char *s) +{ + if (!s) + return -EINVAL; + + if (!strcmp(s, "standard")) + io_delay = standard_io_delay; + else if (!strcmp(s, "alternate")) + io_delay = alternate_io_delay; + else if (!strcmp(s, "udelay")) + io_delay = udelay_io_delay; + else + return -EINVAL; + +#ifndef CONFIG_UDELAY_IO_DELAY + io_delay_override = 1; +#endif + return 0; +} + +early_param("io_delay", io_delay_param); diff --git a/arch/x86/kernel/setup_32.c b/arch/x86/kernel/setup_32.c index 9c24b45b513c..51bdc0b1b72e 100644 --- a/arch/x86/kernel/setup_32.c +++ b/arch/x86/kernel/setup_32.c @@ -648,6 +648,8 @@ void __init setup_arch(char **cmdline_p) dmi_scan_machine(); + io_delay_init();; + #ifdef CONFIG_X86_GENERICARCH generic_apic_probe(); #endif diff --git a/arch/x86/kernel/setup_64.c b/arch/x86/kernel/setup_64.c index 30d94d1d5f5f..ec976edf0399 100644 --- a/arch/x86/kernel/setup_64.c +++ b/arch/x86/kernel/setup_64.c @@ -311,6 +311,8 @@ void __init setup_arch(char **cmdline_p) dmi_scan_machine(); + io_delay_init(); + #ifdef CONFIG_SMP /* setup to use the static apicid table during kernel startup */ x86_cpu_to_apicid_ptr = (void *)&x86_cpu_to_apicid_init; diff --git a/include/asm-x86/io_32.h b/include/asm-x86/io_32.h index fe881cd1e6f4..a8d25c38b91c 100644 --- a/include/asm-x86/io_32.h +++ b/include/asm-x86/io_32.h @@ -250,10 +250,14 @@ static inline void flush_write_buffers(void) #endif /* __KERNEL__ */ -static inline void native_io_delay(void) +#ifndef CONFIG_UDELAY_IO_DELAY +extern void io_delay_init(void); +#else +static inline void io_delay_init(void) { - asm volatile("outb %%al,$0x80" : : : "memory"); } +#endif +extern void native_io_delay(void); #if defined(CONFIG_PARAVIRT) #include diff --git a/include/asm-x86/io_64.h b/include/asm-x86/io_64.h index a037b0794332..5bebaf961692 100644 --- a/include/asm-x86/io_64.h +++ b/include/asm-x86/io_64.h @@ -35,13 +35,24 @@ * - Arnaldo Carvalho de Melo */ -#define __SLOW_DOWN_IO "\noutb %%al,$0x80" - -#ifdef REALLY_SLOW_IO -#define __FULL_SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO +#ifndef CONFIG_UDELAY_IO_DELAY +extern void io_delay_init(void); #else -#define __FULL_SLOW_DOWN_IO __SLOW_DOWN_IO +static inline void io_delay_init(void) +{ +} #endif +extern void native_io_delay(void); + +static inline void slow_down_io(void) +{ + native_io_delay(); +#ifdef REALLY_SLOW_IO + native_io_delay(); + native_io_delay(); + native_io_delay(); +#endif +} /* * Talk about misusing macros.. @@ -50,21 +61,21 @@ static inline void out##s(unsigned x value, unsigned short port) { #define __OUT2(s,s1,s2) \ -__asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1" +__asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1" : : "a" (value), "Nd" (port)) #define __OUT(s,s1,x) \ -__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "Nd" (port)); } \ -__OUT1(s##_p,x) __OUT2(s,s1,"w") __FULL_SLOW_DOWN_IO : : "a" (value), "Nd" (port));} \ +__OUT1(s,x) __OUT2(s,s1,"w"); } \ +__OUT1(s##_p,x) __OUT2(s,s1,"w"); slow_down_io(); } #define __IN1(s) \ static inline RETURN_TYPE in##s(unsigned short port) { RETURN_TYPE _v; #define __IN2(s,s1,s2) \ -__asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0" +__asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0" : "=a" (_v) : "Nd" (port)) -#define __IN(s,s1,i...) \ -__IN1(s) __IN2(s,s1,"w") : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \ -__IN1(s##_p) __IN2(s,s1,"w") __FULL_SLOW_DOWN_IO : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \ +#define __IN(s,s1) \ +__IN1(s) __IN2(s,s1,"w"); return _v; } \ +__IN1(s##_p) __IN2(s,s1,"w"); slow_down_io(); return _v; } #define __INS(s) \ static inline void ins##s(unsigned short port, void * addr, unsigned long count) \