1 /* 2 * I/O delay strategies for inb_p/outb_p 3 * 4 * Allow for a DMI based override of port 0x80, needed for certain HP laptops 5 * and possibly other systems. Also allow for the gradual elimination of 6 * outb_p/inb_p API uses. 7 */ 8 #include <linux/kernel.h> 9 #include <linux/export.h> 10 #include <linux/delay.h> 11 #include <linux/init.h> 12 #include <linux/dmi.h> 13 #include <linux/io.h> 14 15 int io_delay_type __read_mostly = CONFIG_DEFAULT_IO_DELAY_TYPE; 16 17 static int __initdata io_delay_override; 18 19 /* 20 * Paravirt wants native_io_delay to be a constant. 21 */ 22 void native_io_delay(void) 23 { 24 switch (io_delay_type) { 25 default: 26 case CONFIG_IO_DELAY_TYPE_0X80: 27 asm volatile ("outb %al, $0x80"); 28 break; 29 case CONFIG_IO_DELAY_TYPE_0XED: 30 asm volatile ("outb %al, $0xed"); 31 break; 32 case CONFIG_IO_DELAY_TYPE_UDELAY: 33 /* 34 * 2 usecs is an upper-bound for the outb delay but 35 * note that udelay doesn't have the bus-level 36 * side-effects that outb does, nor does udelay() have 37 * precise timings during very early bootup (the delays 38 * are shorter until calibrated): 39 */ 40 udelay(2); 41 case CONFIG_IO_DELAY_TYPE_NONE: 42 break; 43 } 44 } 45 EXPORT_SYMBOL(native_io_delay); 46 47 static int __init dmi_io_delay_0xed_port(const struct dmi_system_id *id) 48 { 49 if (io_delay_type == CONFIG_IO_DELAY_TYPE_0X80) { 50 pr_notice("%s: using 0xed I/O delay port\n", id->ident); 51 io_delay_type = CONFIG_IO_DELAY_TYPE_0XED; 52 } 53 54 return 0; 55 } 56 57 /* 58 * Quirk table for systems that misbehave (lock up, etc.) if port 59 * 0x80 is used: 60 */ 61 static struct dmi_system_id __initdata io_delay_0xed_port_dmi_table[] = { 62 { 63 .callback = dmi_io_delay_0xed_port, 64 .ident = "Compaq Presario V6000", 65 .matches = { 66 DMI_MATCH(DMI_BOARD_VENDOR, "Quanta"), 67 DMI_MATCH(DMI_BOARD_NAME, "30B7") 68 } 69 }, 70 { 71 .callback = dmi_io_delay_0xed_port, 72 .ident = "HP Pavilion dv9000z", 73 .matches = { 74 DMI_MATCH(DMI_BOARD_VENDOR, "Quanta"), 75 DMI_MATCH(DMI_BOARD_NAME, "30B9") 76 } 77 }, 78 { 79 .callback = dmi_io_delay_0xed_port, 80 .ident = "HP Pavilion dv6000", 81 .matches = { 82 DMI_MATCH(DMI_BOARD_VENDOR, "Quanta"), 83 DMI_MATCH(DMI_BOARD_NAME, "30B8") 84 } 85 }, 86 { 87 .callback = dmi_io_delay_0xed_port, 88 .ident = "HP Pavilion tx1000", 89 .matches = { 90 DMI_MATCH(DMI_BOARD_VENDOR, "Quanta"), 91 DMI_MATCH(DMI_BOARD_NAME, "30BF") 92 } 93 }, 94 { 95 .callback = dmi_io_delay_0xed_port, 96 .ident = "Presario F700", 97 .matches = { 98 DMI_MATCH(DMI_BOARD_VENDOR, "Quanta"), 99 DMI_MATCH(DMI_BOARD_NAME, "30D3") 100 } 101 }, 102 { } 103 }; 104 105 void __init io_delay_init(void) 106 { 107 if (!io_delay_override) 108 dmi_check_system(io_delay_0xed_port_dmi_table); 109 } 110 111 static int __init io_delay_param(char *s) 112 { 113 if (!s) 114 return -EINVAL; 115 116 if (!strcmp(s, "0x80")) 117 io_delay_type = CONFIG_IO_DELAY_TYPE_0X80; 118 else if (!strcmp(s, "0xed")) 119 io_delay_type = CONFIG_IO_DELAY_TYPE_0XED; 120 else if (!strcmp(s, "udelay")) 121 io_delay_type = CONFIG_IO_DELAY_TYPE_UDELAY; 122 else if (!strcmp(s, "none")) 123 io_delay_type = CONFIG_IO_DELAY_TYPE_NONE; 124 else 125 return -EINVAL; 126 127 io_delay_override = 1; 128 return 0; 129 } 130 131 early_param("io_delay", io_delay_param); 132