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 #define IO_DELAY_TYPE_0X80 0 17 #define IO_DELAY_TYPE_0XED 1 18 #define IO_DELAY_TYPE_UDELAY 2 19 #define IO_DELAY_TYPE_NONE 3 20 21 #if defined(CONFIG_IO_DELAY_0X80) 22 #define DEFAULT_IO_DELAY_TYPE IO_DELAY_TYPE_0X80 23 #elif defined(CONFIG_IO_DELAY_0XED) 24 #define DEFAULT_IO_DELAY_TYPE IO_DELAY_TYPE_0XED 25 #elif defined(CONFIG_IO_DELAY_UDELAY) 26 #define DEFAULT_IO_DELAY_TYPE IO_DELAY_TYPE_UDELAY 27 #elif defined(CONFIG_IO_DELAY_NONE) 28 #define DEFAULT_IO_DELAY_TYPE IO_DELAY_TYPE_NONE 29 #endif 30 31 int io_delay_type __read_mostly = DEFAULT_IO_DELAY_TYPE; 32 33 static int __initdata io_delay_override; 34 35 /* 36 * Paravirt wants native_io_delay to be a constant. 37 */ 38 void native_io_delay(void) 39 { 40 switch (io_delay_type) { 41 default: 42 case IO_DELAY_TYPE_0X80: 43 asm volatile ("outb %al, $0x80"); 44 break; 45 case IO_DELAY_TYPE_0XED: 46 asm volatile ("outb %al, $0xed"); 47 break; 48 case IO_DELAY_TYPE_UDELAY: 49 /* 50 * 2 usecs is an upper-bound for the outb delay but 51 * note that udelay doesn't have the bus-level 52 * side-effects that outb does, nor does udelay() have 53 * precise timings during very early bootup (the delays 54 * are shorter until calibrated): 55 */ 56 udelay(2); 57 break; 58 case IO_DELAY_TYPE_NONE: 59 break; 60 } 61 } 62 EXPORT_SYMBOL(native_io_delay); 63 64 static int __init dmi_io_delay_0xed_port(const struct dmi_system_id *id) 65 { 66 if (io_delay_type == IO_DELAY_TYPE_0X80) { 67 pr_notice("%s: using 0xed I/O delay port\n", id->ident); 68 io_delay_type = IO_DELAY_TYPE_0XED; 69 } 70 71 return 0; 72 } 73 74 /* 75 * Quirk table for systems that misbehave (lock up, etc.) if port 76 * 0x80 is used: 77 */ 78 static const struct dmi_system_id io_delay_0xed_port_dmi_table[] __initconst = { 79 { 80 .callback = dmi_io_delay_0xed_port, 81 .ident = "Compaq Presario V6000", 82 .matches = { 83 DMI_MATCH(DMI_BOARD_VENDOR, "Quanta"), 84 DMI_MATCH(DMI_BOARD_NAME, "30B7") 85 } 86 }, 87 { 88 .callback = dmi_io_delay_0xed_port, 89 .ident = "HP Pavilion dv9000z", 90 .matches = { 91 DMI_MATCH(DMI_BOARD_VENDOR, "Quanta"), 92 DMI_MATCH(DMI_BOARD_NAME, "30B9") 93 } 94 }, 95 { 96 .callback = dmi_io_delay_0xed_port, 97 .ident = "HP Pavilion dv6000", 98 .matches = { 99 DMI_MATCH(DMI_BOARD_VENDOR, "Quanta"), 100 DMI_MATCH(DMI_BOARD_NAME, "30B8") 101 } 102 }, 103 { 104 .callback = dmi_io_delay_0xed_port, 105 .ident = "HP Pavilion tx1000", 106 .matches = { 107 DMI_MATCH(DMI_BOARD_VENDOR, "Quanta"), 108 DMI_MATCH(DMI_BOARD_NAME, "30BF") 109 } 110 }, 111 { 112 .callback = dmi_io_delay_0xed_port, 113 .ident = "Presario F700", 114 .matches = { 115 DMI_MATCH(DMI_BOARD_VENDOR, "Quanta"), 116 DMI_MATCH(DMI_BOARD_NAME, "30D3") 117 } 118 }, 119 { } 120 }; 121 122 void __init io_delay_init(void) 123 { 124 if (!io_delay_override) 125 dmi_check_system(io_delay_0xed_port_dmi_table); 126 } 127 128 static int __init io_delay_param(char *s) 129 { 130 if (!s) 131 return -EINVAL; 132 133 if (!strcmp(s, "0x80")) 134 io_delay_type = IO_DELAY_TYPE_0X80; 135 else if (!strcmp(s, "0xed")) 136 io_delay_type = IO_DELAY_TYPE_0XED; 137 else if (!strcmp(s, "udelay")) 138 io_delay_type = IO_DELAY_TYPE_UDELAY; 139 else if (!strcmp(s, "none")) 140 io_delay_type = IO_DELAY_TYPE_NONE; 141 else 142 return -EINVAL; 143 144 io_delay_override = 1; 145 return 0; 146 } 147 148 early_param("io_delay", io_delay_param); 149