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