Merge tag 'edac_for_4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp
[linux-2.6-microblaze.git] / drivers / acpi / osi.c
1 /*
2  *  osi.c - _OSI implementation
3  *
4  *  Copyright (C) 2016 Intel Corporation
5  *    Author: Lv Zheng <lv.zheng@intel.com>
6  *
7  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or (at
12  *  your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful, but
15  *  WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  General Public License for more details.
18  *
19  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20  */
21
22 /* Uncomment next line to get verbose printout */
23 /* #define DEBUG */
24 #define pr_fmt(fmt) "ACPI: " fmt
25
26 #include <linux/module.h>
27 #include <linux/kernel.h>
28 #include <linux/acpi.h>
29 #include <linux/dmi.h>
30 #include <linux/platform_data/x86/apple.h>
31
32 #include "internal.h"
33
34
35 #define OSI_STRING_LENGTH_MAX   64
36 #define OSI_STRING_ENTRIES_MAX  16
37
38 struct acpi_osi_entry {
39         char string[OSI_STRING_LENGTH_MAX];
40         bool enable;
41 };
42
43 static struct acpi_osi_config {
44         u8              default_disabling;
45         unsigned int    linux_enable:1;
46         unsigned int    linux_dmi:1;
47         unsigned int    linux_cmdline:1;
48         unsigned int    darwin_enable:1;
49         unsigned int    darwin_dmi:1;
50         unsigned int    darwin_cmdline:1;
51 } osi_config;
52
53 static struct acpi_osi_config osi_config;
54 static struct acpi_osi_entry
55 osi_setup_entries[OSI_STRING_ENTRIES_MAX] __initdata = {
56         {"Module Device", true},
57         {"Processor Device", true},
58         {"3.0 _SCP Extensions", true},
59         {"Processor Aggregator Device", true},
60         /*
61          * Linux-Dell-Video is used by BIOS to disable RTD3 for NVidia graphics
62          * cards as RTD3 is not supported by drivers now.  Systems with NVidia
63          * cards will hang without RTD3 disabled.
64          *
65          * Once NVidia drivers officially support RTD3, this _OSI strings can
66          * be removed if both new and old graphics cards are supported.
67          */
68         {"Linux-Dell-Video", true},
69 };
70
71 static u32 acpi_osi_handler(acpi_string interface, u32 supported)
72 {
73         if (!strcmp("Linux", interface)) {
74                 pr_notice_once(FW_BUG
75                         "BIOS _OSI(Linux) query %s%s\n",
76                         osi_config.linux_enable ? "honored" : "ignored",
77                         osi_config.linux_cmdline ? " via cmdline" :
78                         osi_config.linux_dmi ? " via DMI" : "");
79         }
80         if (!strcmp("Darwin", interface)) {
81                 pr_notice_once(
82                         "BIOS _OSI(Darwin) query %s%s\n",
83                         osi_config.darwin_enable ? "honored" : "ignored",
84                         osi_config.darwin_cmdline ? " via cmdline" :
85                         osi_config.darwin_dmi ? " via DMI" : "");
86         }
87
88         return supported;
89 }
90
91 void __init acpi_osi_setup(char *str)
92 {
93         struct acpi_osi_entry *osi;
94         bool enable = true;
95         int i;
96
97         if (!acpi_gbl_create_osi_method)
98                 return;
99
100         if (str == NULL || *str == '\0') {
101                 pr_info("_OSI method disabled\n");
102                 acpi_gbl_create_osi_method = FALSE;
103                 return;
104         }
105
106         if (*str == '!') {
107                 str++;
108                 if (*str == '\0') {
109                         /* Do not override acpi_osi=!* */
110                         if (!osi_config.default_disabling)
111                                 osi_config.default_disabling =
112                                         ACPI_DISABLE_ALL_VENDOR_STRINGS;
113                         return;
114                 } else if (*str == '*') {
115                         osi_config.default_disabling = ACPI_DISABLE_ALL_STRINGS;
116                         for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
117                                 osi = &osi_setup_entries[i];
118                                 osi->enable = false;
119                         }
120                         return;
121                 } else if (*str == '!') {
122                         osi_config.default_disabling = 0;
123                         return;
124                 }
125                 enable = false;
126         }
127
128         for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
129                 osi = &osi_setup_entries[i];
130                 if (!strcmp(osi->string, str)) {
131                         osi->enable = enable;
132                         break;
133                 } else if (osi->string[0] == '\0') {
134                         osi->enable = enable;
135                         strncpy(osi->string, str, OSI_STRING_LENGTH_MAX);
136                         break;
137                 }
138         }
139 }
140
141 static void __init __acpi_osi_setup_darwin(bool enable)
142 {
143         osi_config.darwin_enable = !!enable;
144         if (enable) {
145                 acpi_osi_setup("!");
146                 acpi_osi_setup("Darwin");
147         } else {
148                 acpi_osi_setup("!!");
149                 acpi_osi_setup("!Darwin");
150         }
151 }
152
153 static void __init acpi_osi_setup_darwin(bool enable)
154 {
155         /* Override acpi_osi_dmi_blacklisted() */
156         osi_config.darwin_dmi = 0;
157         osi_config.darwin_cmdline = 1;
158         __acpi_osi_setup_darwin(enable);
159 }
160
161 /*
162  * The story of _OSI(Linux)
163  *
164  * From pre-history through Linux-2.6.22, Linux responded TRUE upon a BIOS
165  * OSI(Linux) query.
166  *
167  * Unfortunately, reference BIOS writers got wind of this and put
168  * OSI(Linux) in their example code, quickly exposing this string as
169  * ill-conceived and opening the door to an un-bounded number of BIOS
170  * incompatibilities.
171  *
172  * For example, OSI(Linux) was used on resume to re-POST a video card on
173  * one system, because Linux at that time could not do a speedy restore in
174  * its native driver. But then upon gaining quick native restore
175  * capability, Linux has no way to tell the BIOS to skip the time-consuming
176  * POST -- putting Linux at a permanent performance disadvantage. On
177  * another system, the BIOS writer used OSI(Linux) to infer native OS
178  * support for IPMI!  On other systems, OSI(Linux) simply got in the way of
179  * Linux claiming to be compatible with other operating systems, exposing
180  * BIOS issues such as skipped device initialization.
181  *
182  * So "Linux" turned out to be a really poor chose of OSI string, and from
183  * Linux-2.6.23 onward we respond FALSE.
184  *
185  * BIOS writers should NOT query _OSI(Linux) on future systems. Linux will
186  * complain on the console when it sees it, and return FALSE. To get Linux
187  * to return TRUE for your system  will require a kernel source update to
188  * add a DMI entry, or boot with "acpi_osi=Linux"
189  */
190 static void __init __acpi_osi_setup_linux(bool enable)
191 {
192         osi_config.linux_enable = !!enable;
193         if (enable)
194                 acpi_osi_setup("Linux");
195         else
196                 acpi_osi_setup("!Linux");
197 }
198
199 static void __init acpi_osi_setup_linux(bool enable)
200 {
201         /* Override acpi_osi_dmi_blacklisted() */
202         osi_config.linux_dmi = 0;
203         osi_config.linux_cmdline = 1;
204         __acpi_osi_setup_linux(enable);
205 }
206
207 /*
208  * Modify the list of "OS Interfaces" reported to BIOS via _OSI
209  *
210  * empty string disables _OSI
211  * string starting with '!' disables that string
212  * otherwise string is added to list, augmenting built-in strings
213  */
214 static void __init acpi_osi_setup_late(void)
215 {
216         struct acpi_osi_entry *osi;
217         char *str;
218         int i;
219         acpi_status status;
220
221         if (osi_config.default_disabling) {
222                 status = acpi_update_interfaces(osi_config.default_disabling);
223                 if (ACPI_SUCCESS(status))
224                         pr_info("Disabled all _OSI OS vendors%s\n",
225                                 osi_config.default_disabling ==
226                                 ACPI_DISABLE_ALL_STRINGS ?
227                                 " and feature groups" : "");
228         }
229
230         for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
231                 osi = &osi_setup_entries[i];
232                 str = osi->string;
233                 if (*str == '\0')
234                         break;
235                 if (osi->enable) {
236                         status = acpi_install_interface(str);
237                         if (ACPI_SUCCESS(status))
238                                 pr_info("Added _OSI(%s)\n", str);
239                 } else {
240                         status = acpi_remove_interface(str);
241                         if (ACPI_SUCCESS(status))
242                                 pr_info("Deleted _OSI(%s)\n", str);
243                 }
244         }
245 }
246
247 static int __init osi_setup(char *str)
248 {
249         if (str && !strcmp("Linux", str))
250                 acpi_osi_setup_linux(true);
251         else if (str && !strcmp("!Linux", str))
252                 acpi_osi_setup_linux(false);
253         else if (str && !strcmp("Darwin", str))
254                 acpi_osi_setup_darwin(true);
255         else if (str && !strcmp("!Darwin", str))
256                 acpi_osi_setup_darwin(false);
257         else
258                 acpi_osi_setup(str);
259
260         return 1;
261 }
262 __setup("acpi_osi=", osi_setup);
263
264 bool acpi_osi_is_win8(void)
265 {
266         return acpi_gbl_osi_data >= ACPI_OSI_WIN_8;
267 }
268 EXPORT_SYMBOL(acpi_osi_is_win8);
269
270 static void __init acpi_osi_dmi_darwin(void)
271 {
272         pr_notice("DMI detected to setup _OSI(\"Darwin\"): Apple hardware\n");
273         osi_config.darwin_dmi = 1;
274         __acpi_osi_setup_darwin(true);
275 }
276
277 static void __init acpi_osi_dmi_linux(bool enable,
278                                       const struct dmi_system_id *d)
279 {
280         pr_notice("DMI detected to setup _OSI(\"Linux\"): %s\n", d->ident);
281         osi_config.linux_dmi = 1;
282         __acpi_osi_setup_linux(enable);
283 }
284
285 static int __init dmi_enable_osi_linux(const struct dmi_system_id *d)
286 {
287         acpi_osi_dmi_linux(true, d);
288
289         return 0;
290 }
291
292 static int __init dmi_disable_osi_vista(const struct dmi_system_id *d)
293 {
294         pr_notice("DMI detected: %s\n", d->ident);
295         acpi_osi_setup("!Windows 2006");
296         acpi_osi_setup("!Windows 2006 SP1");
297         acpi_osi_setup("!Windows 2006 SP2");
298
299         return 0;
300 }
301
302 static int __init dmi_disable_osi_win7(const struct dmi_system_id *d)
303 {
304         pr_notice("DMI detected: %s\n", d->ident);
305         acpi_osi_setup("!Windows 2009");
306
307         return 0;
308 }
309
310 static int __init dmi_disable_osi_win8(const struct dmi_system_id *d)
311 {
312         pr_notice("DMI detected: %s\n", d->ident);
313         acpi_osi_setup("!Windows 2012");
314
315         return 0;
316 }
317
318 /*
319  * Linux default _OSI response behavior is determined by this DMI table.
320  *
321  * Note that _OSI("Linux")/_OSI("Darwin") determined here can be overridden
322  * by acpi_osi=!Linux/acpi_osi=!Darwin command line options.
323  */
324 static const struct dmi_system_id acpi_osi_dmi_table[] __initconst = {
325         {
326         .callback = dmi_disable_osi_vista,
327         .ident = "Fujitsu Siemens",
328         .matches = {
329                      DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
330                      DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"),
331                 },
332         },
333         {
334         /*
335          * There have a NVIF method in MSI GX723 DSDT need call by Nvidia
336          * driver (e.g. nouveau) when user press brightness hotkey.
337          * Currently, nouveau driver didn't do the job and it causes there
338          * have a infinite while loop in DSDT when user press hotkey.
339          * We add MSI GX723's dmi information to this table for workaround
340          * this issue.
341          * Will remove MSI GX723 from the table after nouveau grows support.
342          */
343         .callback = dmi_disable_osi_vista,
344         .ident = "MSI GX723",
345         .matches = {
346                      DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
347                      DMI_MATCH(DMI_PRODUCT_NAME, "GX723"),
348                 },
349         },
350         {
351         .callback = dmi_disable_osi_vista,
352         .ident = "Sony VGN-NS10J_S",
353         .matches = {
354                      DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
355                      DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS10J_S"),
356                 },
357         },
358         {
359         .callback = dmi_disable_osi_vista,
360         .ident = "Sony VGN-SR290J",
361         .matches = {
362                      DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
363                      DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR290J"),
364                 },
365         },
366         {
367         .callback = dmi_disable_osi_vista,
368         .ident = "VGN-NS50B_L",
369         .matches = {
370                      DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
371                      DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS50B_L"),
372                 },
373         },
374         {
375         .callback = dmi_disable_osi_vista,
376         .ident = "VGN-SR19XN",
377         .matches = {
378                      DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
379                      DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR19XN"),
380                 },
381         },
382         {
383         .callback = dmi_disable_osi_vista,
384         .ident = "Toshiba Satellite L355",
385         .matches = {
386                      DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
387                      DMI_MATCH(DMI_PRODUCT_VERSION, "Satellite L355"),
388                 },
389         },
390         {
391         .callback = dmi_disable_osi_win7,
392         .ident = "ASUS K50IJ",
393         .matches = {
394                      DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
395                      DMI_MATCH(DMI_PRODUCT_NAME, "K50IJ"),
396                 },
397         },
398         {
399         .callback = dmi_disable_osi_vista,
400         .ident = "Toshiba P305D",
401         .matches = {
402                      DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
403                      DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P305D"),
404                 },
405         },
406         {
407         .callback = dmi_disable_osi_vista,
408         .ident = "Toshiba NB100",
409         .matches = {
410                      DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
411                      DMI_MATCH(DMI_PRODUCT_NAME, "NB100"),
412                 },
413         },
414
415         /*
416          * The wireless hotkey does not work on those machines when
417          * returning true for _OSI("Windows 2012")
418          */
419         {
420         .callback = dmi_disable_osi_win8,
421         .ident = "Dell Inspiron 7737",
422         .matches = {
423                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
424                     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7737"),
425                 },
426         },
427         {
428         .callback = dmi_disable_osi_win8,
429         .ident = "Dell Inspiron 7537",
430         .matches = {
431                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
432                     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7537"),
433                 },
434         },
435         {
436         .callback = dmi_disable_osi_win8,
437         .ident = "Dell Inspiron 5437",
438         .matches = {
439                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
440                     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5437"),
441                 },
442         },
443         {
444         .callback = dmi_disable_osi_win8,
445         .ident = "Dell Inspiron 3437",
446         .matches = {
447                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
448                     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 3437"),
449                 },
450         },
451         {
452         .callback = dmi_disable_osi_win8,
453         .ident = "Dell Vostro 3446",
454         .matches = {
455                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
456                     DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3446"),
457                 },
458         },
459         {
460         .callback = dmi_disable_osi_win8,
461         .ident = "Dell Vostro 3546",
462         .matches = {
463                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
464                     DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3546"),
465                 },
466         },
467
468         /*
469          * BIOS invocation of _OSI(Linux) is almost always a BIOS bug.
470          * Linux ignores it, except for the machines enumerated below.
471          */
472
473         /*
474          * Without this this EEEpc exports a non working WMI interface, with
475          * this it exports a working "good old" eeepc_laptop interface, fixing
476          * both brightness control, and rfkill not working.
477          */
478         {
479         .callback = dmi_enable_osi_linux,
480         .ident = "Asus EEE PC 1015PX",
481         .matches = {
482                      DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
483                      DMI_MATCH(DMI_PRODUCT_NAME, "1015PX"),
484                 },
485         },
486         {}
487 };
488
489 static __init void acpi_osi_dmi_blacklisted(void)
490 {
491         dmi_check_system(acpi_osi_dmi_table);
492
493         /* Enable _OSI("Darwin") for Apple platforms. */
494         if (x86_apple_machine)
495                 acpi_osi_dmi_darwin();
496 }
497
498 int __init early_acpi_osi_init(void)
499 {
500         acpi_osi_dmi_blacklisted();
501
502         return 0;
503 }
504
505 int __init acpi_osi_init(void)
506 {
507         acpi_install_interface_handler(acpi_osi_handler);
508         acpi_osi_setup_late();
509
510         return 0;
511 }