diff options
Diffstat (limited to 'drivers/acpi/osl.c')
-rw-r--r-- | drivers/acpi/osl.c | 223 |
1 files changed, 144 insertions, 79 deletions
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 82525d9cccb0..0c41db3075a5 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c | |||
@@ -77,11 +77,55 @@ static struct workqueue_struct *kacpi_notify_wq; | |||
77 | #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ | 77 | #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ |
78 | static char osi_additional_string[OSI_STRING_LENGTH_MAX]; | 78 | static char osi_additional_string[OSI_STRING_LENGTH_MAX]; |
79 | 79 | ||
80 | static int osi_linux; /* disable _OSI(Linux) by default */ | 80 | /* |
81 | * "Ode to _OSI(Linux)" | ||
82 | * | ||
83 | * osi_linux -- Control response to BIOS _OSI(Linux) query. | ||
84 | * | ||
85 | * As Linux evolves, the features that it supports change. | ||
86 | * So an OSI string such as "Linux" is not specific enough | ||
87 | * to be useful across multiple versions of Linux. It | ||
88 | * doesn't identify any particular feature, interface, | ||
89 | * or even any particular version of Linux... | ||
90 | * | ||
91 | * Unfortunately, Linux-2.6.22 and earlier responded "yes" | ||
92 | * to a BIOS _OSI(Linux) query. When | ||
93 | * a reference mobile BIOS started using it, its use | ||
94 | * started to spread to many vendor platforms. | ||
95 | * As it is not supportable, we need to halt that spread. | ||
96 | * | ||
97 | * Today, most BIOS references to _OSI(Linux) are noise -- | ||
98 | * they have no functional effect and are just dead code | ||
99 | * carried over from the reference BIOS. | ||
100 | * | ||
101 | * The next most common case is that _OSI(Linux) harms Linux, | ||
102 | * usually by causing the BIOS to follow paths that are | ||
103 | * not tested during Windows validation. | ||
104 | * | ||
105 | * Finally, there is a short list of platforms | ||
106 | * where OSI(Linux) benefits Linux. | ||
107 | * | ||
108 | * In Linux-2.6.23, OSI(Linux) is first disabled by default. | ||
109 | * DMI is used to disable the dmesg warning about OSI(Linux) | ||
110 | * on platforms where it is known to have no effect. | ||
111 | * But a dmesg warning remains for systems where | ||
112 | * we do not know if OSI(Linux) is good or bad for the system. | ||
113 | * DMI is also used to enable OSI(Linux) for the machines | ||
114 | * that are known to need it. | ||
115 | * | ||
116 | * BIOS writers should NOT query _OSI(Linux) on future systems. | ||
117 | * It will be ignored by default, and to get Linux to | ||
118 | * not ignore it will require a kernel source update to | ||
119 | * add a DMI entry, or a boot-time "acpi_osi=Linux" invocation. | ||
120 | */ | ||
121 | #define OSI_LINUX_ENABLE 0 | ||
81 | 122 | ||
82 | #ifdef CONFIG_DMI | 123 | static struct osi_linux { |
83 | static struct __initdata dmi_system_id acpi_osl_dmi_table[]; | 124 | unsigned int enable:1; |
84 | #endif | 125 | unsigned int dmi:1; |
126 | unsigned int cmdline:1; | ||
127 | unsigned int known:1; | ||
128 | } osi_linux = { OSI_LINUX_ENABLE, 0, 0, 0}; | ||
85 | 129 | ||
86 | static void __init acpi_request_region (struct acpi_generic_address *addr, | 130 | static void __init acpi_request_region (struct acpi_generic_address *addr, |
87 | unsigned int length, char *desc) | 131 | unsigned int length, char *desc) |
@@ -133,7 +177,6 @@ device_initcall(acpi_reserve_resources); | |||
133 | 177 | ||
134 | acpi_status __init acpi_os_initialize(void) | 178 | acpi_status __init acpi_os_initialize(void) |
135 | { | 179 | { |
136 | dmi_check_system(acpi_osl_dmi_table); | ||
137 | return AE_OK; | 180 | return AE_OK; |
138 | } | 181 | } |
139 | 182 | ||
@@ -207,8 +250,12 @@ acpi_physical_address __init acpi_os_get_root_pointer(void) | |||
207 | "System description tables not found\n"); | 250 | "System description tables not found\n"); |
208 | return 0; | 251 | return 0; |
209 | } | 252 | } |
210 | } else | 253 | } else { |
211 | return acpi_find_rsdp(); | 254 | acpi_physical_address pa = 0; |
255 | |||
256 | acpi_find_root_pointer(&pa); | ||
257 | return pa; | ||
258 | } | ||
212 | } | 259 | } |
213 | 260 | ||
214 | void __iomem *__init_refok | 261 | void __iomem *__init_refok |
@@ -619,25 +666,6 @@ static void acpi_os_execute_deferred(struct work_struct *work) | |||
619 | dpc->function(dpc->context); | 666 | dpc->function(dpc->context); |
620 | kfree(dpc); | 667 | kfree(dpc); |
621 | 668 | ||
622 | /* Yield cpu to notify thread */ | ||
623 | cond_resched(); | ||
624 | |||
625 | return; | ||
626 | } | ||
627 | |||
628 | static void acpi_os_execute_notify(struct work_struct *work) | ||
629 | { | ||
630 | struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work); | ||
631 | |||
632 | if (!dpc) { | ||
633 | printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); | ||
634 | return; | ||
635 | } | ||
636 | |||
637 | dpc->function(dpc->context); | ||
638 | |||
639 | kfree(dpc); | ||
640 | |||
641 | return; | 669 | return; |
642 | } | 670 | } |
643 | 671 | ||
@@ -661,7 +689,7 @@ acpi_status acpi_os_execute(acpi_execute_type type, | |||
661 | { | 689 | { |
662 | acpi_status status = AE_OK; | 690 | acpi_status status = AE_OK; |
663 | struct acpi_os_dpc *dpc; | 691 | struct acpi_os_dpc *dpc; |
664 | 692 | struct workqueue_struct *queue; | |
665 | ACPI_DEBUG_PRINT((ACPI_DB_EXEC, | 693 | ACPI_DEBUG_PRINT((ACPI_DB_EXEC, |
666 | "Scheduling function [%p(%p)] for deferred execution.\n", | 694 | "Scheduling function [%p(%p)] for deferred execution.\n", |
667 | function, context)); | 695 | function, context)); |
@@ -685,20 +713,13 @@ acpi_status acpi_os_execute(acpi_execute_type type, | |||
685 | dpc->function = function; | 713 | dpc->function = function; |
686 | dpc->context = context; | 714 | dpc->context = context; |
687 | 715 | ||
688 | if (type == OSL_NOTIFY_HANDLER) { | 716 | INIT_WORK(&dpc->work, acpi_os_execute_deferred); |
689 | INIT_WORK(&dpc->work, acpi_os_execute_notify); | 717 | queue = (type == OSL_NOTIFY_HANDLER) ? kacpi_notify_wq : kacpid_wq; |
690 | if (!queue_work(kacpi_notify_wq, &dpc->work)) { | 718 | if (!queue_work(queue, &dpc->work)) { |
691 | status = AE_ERROR; | 719 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, |
692 | kfree(dpc); | 720 | "Call to queue_work() failed.\n")); |
693 | } | 721 | status = AE_ERROR; |
694 | } else { | 722 | kfree(dpc); |
695 | INIT_WORK(&dpc->work, acpi_os_execute_deferred); | ||
696 | if (!queue_work(kacpid_wq, &dpc->work)) { | ||
697 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
698 | "Call to queue_work() failed.\n")); | ||
699 | status = AE_ERROR; | ||
700 | kfree(dpc); | ||
701 | } | ||
702 | } | 723 | } |
703 | return_ACPI_STATUS(status); | 724 | return_ACPI_STATUS(status); |
704 | } | 725 | } |
@@ -965,13 +986,37 @@ static int __init acpi_os_name_setup(char *str) | |||
965 | 986 | ||
966 | __setup("acpi_os_name=", acpi_os_name_setup); | 987 | __setup("acpi_os_name=", acpi_os_name_setup); |
967 | 988 | ||
968 | static void enable_osi_linux(int enable) { | 989 | static void __init set_osi_linux(unsigned int enable) |
990 | { | ||
991 | if (osi_linux.enable != enable) { | ||
992 | osi_linux.enable = enable; | ||
993 | printk(KERN_NOTICE PREFIX "%sed _OSI(Linux)\n", | ||
994 | enable ? "Add": "Delet"); | ||
995 | } | ||
996 | return; | ||
997 | } | ||
998 | |||
999 | static void __init acpi_cmdline_osi_linux(unsigned int enable) | ||
1000 | { | ||
1001 | osi_linux.cmdline = 1; /* cmdline set the default */ | ||
1002 | set_osi_linux(enable); | ||
1003 | |||
1004 | return; | ||
1005 | } | ||
1006 | |||
1007 | void __init acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d) | ||
1008 | { | ||
1009 | osi_linux.dmi = 1; /* DMI knows that this box asks OSI(Linux) */ | ||
1010 | |||
1011 | printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); | ||
1012 | |||
1013 | if (enable == -1) | ||
1014 | return; | ||
1015 | |||
1016 | osi_linux.known = 1; /* DMI knows which OSI(Linux) default needed */ | ||
969 | 1017 | ||
970 | if (osi_linux != enable) | 1018 | set_osi_linux(enable); |
971 | printk(KERN_INFO PREFIX "%sabled _OSI(Linux)\n", | ||
972 | enable ? "En": "Dis"); | ||
973 | 1019 | ||
974 | osi_linux = enable; | ||
975 | return; | 1020 | return; |
976 | } | 1021 | } |
977 | 1022 | ||
@@ -988,12 +1033,12 @@ static int __init acpi_osi_setup(char *str) | |||
988 | printk(KERN_INFO PREFIX "_OSI method disabled\n"); | 1033 | printk(KERN_INFO PREFIX "_OSI method disabled\n"); |
989 | acpi_gbl_create_osi_method = FALSE; | 1034 | acpi_gbl_create_osi_method = FALSE; |
990 | } else if (!strcmp("!Linux", str)) { | 1035 | } else if (!strcmp("!Linux", str)) { |
991 | enable_osi_linux(0); | 1036 | acpi_cmdline_osi_linux(0); /* !enable */ |
992 | } else if (*str == '!') { | 1037 | } else if (*str == '!') { |
993 | if (acpi_osi_invalidate(++str) == AE_OK) | 1038 | if (acpi_osi_invalidate(++str) == AE_OK) |
994 | printk(KERN_INFO PREFIX "Deleted _OSI(%s)\n", str); | 1039 | printk(KERN_INFO PREFIX "Deleted _OSI(%s)\n", str); |
995 | } else if (!strcmp("Linux", str)) { | 1040 | } else if (!strcmp("Linux", str)) { |
996 | enable_osi_linux(1); | 1041 | acpi_cmdline_osi_linux(1); /* enable */ |
997 | } else if (*osi_additional_string == '\0') { | 1042 | } else if (*osi_additional_string == '\0') { |
998 | strncpy(osi_additional_string, str, OSI_STRING_LENGTH_MAX); | 1043 | strncpy(osi_additional_string, str, OSI_STRING_LENGTH_MAX); |
999 | printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str); | 1044 | printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str); |
@@ -1142,6 +1187,34 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object) | |||
1142 | return (AE_OK); | 1187 | return (AE_OK); |
1143 | } | 1188 | } |
1144 | 1189 | ||
1190 | /** | ||
1191 | * acpi_dmi_dump - dump DMI slots needed for blacklist entry | ||
1192 | * | ||
1193 | * Returns 0 on success | ||
1194 | */ | ||
1195 | static int acpi_dmi_dump(void) | ||
1196 | { | ||
1197 | |||
1198 | if (!dmi_available) | ||
1199 | return -1; | ||
1200 | |||
1201 | printk(KERN_NOTICE PREFIX "DMI System Vendor: %s\n", | ||
1202 | dmi_get_system_info(DMI_SYS_VENDOR)); | ||
1203 | printk(KERN_NOTICE PREFIX "DMI Product Name: %s\n", | ||
1204 | dmi_get_system_info(DMI_PRODUCT_NAME)); | ||
1205 | printk(KERN_NOTICE PREFIX "DMI Product Version: %s\n", | ||
1206 | dmi_get_system_info(DMI_PRODUCT_VERSION)); | ||
1207 | printk(KERN_NOTICE PREFIX "DMI Board Name: %s\n", | ||
1208 | dmi_get_system_info(DMI_BOARD_NAME)); | ||
1209 | printk(KERN_NOTICE PREFIX "DMI BIOS Vendor: %s\n", | ||
1210 | dmi_get_system_info(DMI_BIOS_VENDOR)); | ||
1211 | printk(KERN_NOTICE PREFIX "DMI BIOS Date: %s\n", | ||
1212 | dmi_get_system_info(DMI_BIOS_DATE)); | ||
1213 | |||
1214 | return 0; | ||
1215 | } | ||
1216 | |||
1217 | |||
1145 | /****************************************************************************** | 1218 | /****************************************************************************** |
1146 | * | 1219 | * |
1147 | * FUNCTION: acpi_os_validate_interface | 1220 | * FUNCTION: acpi_os_validate_interface |
@@ -1161,13 +1234,29 @@ acpi_os_validate_interface (char *interface) | |||
1161 | if (!strncmp(osi_additional_string, interface, OSI_STRING_LENGTH_MAX)) | 1234 | if (!strncmp(osi_additional_string, interface, OSI_STRING_LENGTH_MAX)) |
1162 | return AE_OK; | 1235 | return AE_OK; |
1163 | if (!strcmp("Linux", interface)) { | 1236 | if (!strcmp("Linux", interface)) { |
1164 | printk(KERN_WARNING PREFIX | 1237 | |
1165 | "System BIOS is requesting _OSI(Linux)\n"); | 1238 | printk(KERN_NOTICE PREFIX |
1166 | printk(KERN_WARNING PREFIX | 1239 | "BIOS _OSI(Linux) query %s%s\n", |
1167 | "If \"acpi_osi=Linux\" works better,\n" | 1240 | osi_linux.enable ? "honored" : "ignored", |
1168 | "Please send dmidecode " | 1241 | osi_linux.cmdline ? " via cmdline" : |
1169 | "to linux-acpi@vger.kernel.org\n"); | 1242 | osi_linux.dmi ? " via DMI" : ""); |
1170 | if(osi_linux) | 1243 | |
1244 | if (!osi_linux.dmi) { | ||
1245 | if (acpi_dmi_dump()) | ||
1246 | printk(KERN_NOTICE PREFIX | ||
1247 | "[please extract dmidecode output]\n"); | ||
1248 | printk(KERN_NOTICE PREFIX | ||
1249 | "Please send DMI info above to " | ||
1250 | "linux-acpi@vger.kernel.org\n"); | ||
1251 | } | ||
1252 | if (!osi_linux.known && !osi_linux.cmdline) { | ||
1253 | printk(KERN_NOTICE PREFIX | ||
1254 | "If \"acpi_osi=%sLinux\" works better, " | ||
1255 | "please notify linux-acpi@vger.kernel.org\n", | ||
1256 | osi_linux.enable ? "!" : ""); | ||
1257 | } | ||
1258 | |||
1259 | if (osi_linux.enable) | ||
1171 | return AE_OK; | 1260 | return AE_OK; |
1172 | } | 1261 | } |
1173 | return AE_SUPPORT; | 1262 | return AE_SUPPORT; |
@@ -1199,28 +1288,4 @@ acpi_os_validate_address ( | |||
1199 | return AE_OK; | 1288 | return AE_OK; |
1200 | } | 1289 | } |
1201 | 1290 | ||
1202 | #ifdef CONFIG_DMI | ||
1203 | static int dmi_osi_linux(const struct dmi_system_id *d) | ||
1204 | { | ||
1205 | printk(KERN_NOTICE "%s detected: enabling _OSI(Linux)\n", d->ident); | ||
1206 | enable_osi_linux(1); | ||
1207 | return 0; | ||
1208 | } | ||
1209 | |||
1210 | static struct dmi_system_id acpi_osl_dmi_table[] __initdata = { | ||
1211 | /* | ||
1212 | * Boxes that need _OSI(Linux) | ||
1213 | */ | ||
1214 | { | ||
1215 | .callback = dmi_osi_linux, | ||
1216 | .ident = "Intel Napa CRB", | ||
1217 | .matches = { | ||
1218 | DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), | ||
1219 | DMI_MATCH(DMI_BOARD_NAME, "MPAD-MSAE Customer Reference Boards"), | ||
1220 | }, | ||
1221 | }, | ||
1222 | {} | ||
1223 | }; | ||
1224 | #endif /* CONFIG_DMI */ | ||
1225 | |||
1226 | #endif | 1291 | #endif |