x86: various changes and cleanups to in_p/out_p delay details

various changes to the in_p/out_p delay details:

- add the io_delay=none method
- make each method selectable from the kernel config
- simplify the delay code a bit by getting rid of an indirect function call
- add the /proc/sys/kernel/io_delay_type sysctl
- change 'io_delay=standard|alternate' to io_delay=0x80 and io_delay=0xed
- make the io delay config not depend on CONFIG_DEBUG_KERNEL

Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: "David P. Reed" <dpreed@reed.com>
This commit is contained in:
Ingo Molnar 2008-01-30 13:30:05 +01:00
parent b02aae9cf5
commit 6e7c402590
6 changed files with 143 additions and 83 deletions

View file

@ -795,12 +795,14 @@ and is between 256 and 4096 characters. It is defined in the file
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
0x80
Standard port 0x80 based delay
0xed
Alternate port 0xed based delay (needed on some systems)
udelay
Simple two microsecond delay
Simple two microseconds delay
none
No delay
io7= [HW] IO7 for Marvel based alpha systems
See comment before marvel_specify_io7 in

View file

@ -112,13 +112,78 @@ 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
#
# IO delay types:
#
config IO_DELAY_TYPE_0X80
int
default "0"
config IO_DELAY_TYPE_0XED
int
default "1"
config IO_DELAY_TYPE_UDELAY
int
default "2"
config IO_DELAY_TYPE_NONE
int
default "3"
choice
prompt "IO delay type"
default IO_DELAY_0X80
config IO_DELAY_0X80
bool "port 0x80 based port-IO delay [recommended]"
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.
This is the traditional Linux IO delay used for in/out_p.
It is the most tested hence safest selection here.
config IO_DELAY_0XED
bool "port 0xed based port-IO delay"
help
Use port 0xed as the IO delay. This frees up port 0x80 which is
often used as a hardware-debug port.
config IO_DELAY_UDELAY
bool "udelay based port-IO delay"
help
Use udelay(2) as the IO delay method. This provides the delay
while not having any side-effect on the IO port space.
config IO_DELAY_NONE
bool "no port-IO delay"
help
No port-IO delay. Will break on old boxes that require port-IO
delay for certain operations. Should work on most new machines.
endchoice
if IO_DELAY_0X80
config DEFAULT_IO_DELAY_TYPE
int
default IO_DELAY_TYPE_0X80
endif
if IO_DELAY_0XED
config DEFAULT_IO_DELAY_TYPE
int
default IO_DELAY_TYPE_0XED
endif
if IO_DELAY_UDELAY
config DEFAULT_IO_DELAY_TYPE
int
default IO_DELAY_TYPE_UDELAY
endif
if IO_DELAY_NONE
config DEFAULT_IO_DELAY_TYPE
int
default IO_DELAY_TYPE_NONE
endif
endmenu

View file

@ -1,5 +1,9 @@
/*
* I/O delay strategies for inb_p/outb_p
*
* Allow for a DMI based override of port 0x80, needed for certain HP laptops
* and possibly other systems. Also allow for the gradual elimination of
* outb_p/inb_p API uses.
*/
#include <linux/kernel.h>
#include <linux/module.h>
@ -8,98 +12,86 @@
#include <linux/dmi.h>
#include <asm/io.h>
/*
* 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
int io_delay_type __read_mostly = CONFIG_DEFAULT_IO_DELAY_TYPE;
EXPORT_SYMBOL_GPL(io_delay_type);
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
static int __initdata io_delay_override;
/*
* Paravirt wants native_io_delay to be a constant.
*/
void native_io_delay(void)
{
io_delay();
switch (io_delay_type) {
default:
case CONFIG_IO_DELAY_TYPE_0X80:
asm volatile ("outb %al, $0x80");
break;
case CONFIG_IO_DELAY_TYPE_0XED:
asm volatile ("outb %al, $0xed");
break;
case CONFIG_IO_DELAY_TYPE_UDELAY:
/*
* 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, nor does udelay() have
* precise timings during very early bootup (the delays
* are shorter until calibrated):
*/
udelay(2);
case CONFIG_IO_DELAY_TYPE_NONE:
break;
}
}
EXPORT_SYMBOL(native_io_delay);
#ifndef CONFIG_UDELAY_IO_DELAY
static int __init dmi_alternate_io_delay_port(const struct dmi_system_id *id)
static int __init dmi_io_delay_0xed_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;
if (io_delay_type == CONFIG_IO_DELAY_TYPE_0X80) {
printk(KERN_NOTICE "%s: using 0xed I/O delay port\n",
id->ident);
io_delay_type = CONFIG_IO_DELAY_TYPE_0XED;
}
return 0;
}
static struct dmi_system_id __initdata alternate_io_delay_port_dmi_table[] = {
/*
* Quirk table for systems that misbehave (lock up, etc.) if port
* 0x80 is used:
*/
static struct dmi_system_id __initdata io_delay_0xed_port_dmi_table[] = {
{
.callback = dmi_alternate_io_delay_port,
.callback = dmi_io_delay_0xed_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);
dmi_check_system(io_delay_0xed_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;
if (!strcmp(s, "0x80"))
io_delay_type = CONFIG_IO_DELAY_TYPE_0X80;
else if (!strcmp(s, "0xed"))
io_delay_type = CONFIG_IO_DELAY_TYPE_0XED;
else if (!strcmp(s, "udelay"))
io_delay = udelay_io_delay;
io_delay_type = CONFIG_IO_DELAY_TYPE_UDELAY;
else if (!strcmp(s, "none"))
io_delay_type = CONFIG_IO_DELAY_TYPE_NONE;
else
return -EINVAL;
#ifndef CONFIG_UDELAY_IO_DELAY
io_delay_override = 1;
#endif
return 0;
}

View file

@ -250,15 +250,11 @@ static inline void flush_write_buffers(void)
#endif /* __KERNEL__ */
#ifndef CONFIG_UDELAY_IO_DELAY
extern void io_delay_init(void);
#else
static inline void io_delay_init(void)
{
}
#endif
extern void native_io_delay(void);
extern int io_delay_type;
extern void io_delay_init(void);
#if defined(CONFIG_PARAVIRT)
#include <asm/paravirt.h>
#else

View file

@ -35,15 +35,11 @@
* - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*/
#ifndef CONFIG_UDELAY_IO_DELAY
extern void io_delay_init(void);
#else
static inline void io_delay_init(void)
{
}
#endif
extern void native_io_delay(void);
extern int io_delay_type;
extern void io_delay_init(void);
static inline void slow_down_io(void)
{
native_io_delay();

View file

@ -53,6 +53,7 @@
#ifdef CONFIG_X86
#include <asm/nmi.h>
#include <asm/stacktrace.h>
#include <asm/io.h>
#endif
static int deprecated_sysctl_warning(struct __sysctl_args *args);
@ -727,6 +728,14 @@ static struct ctl_table kern_table[] = {
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
.ctl_name = CTL_UNNUMBERED,
.procname = "io_delay_type",
.data = &io_delay_type,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
#endif
#if defined(CONFIG_MMU)
{