diff options
-rw-r--r-- | drivers/acpi/Makefile | 1 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 6 | ||||
-rw-r--r-- | drivers/acpi/property.c | 3 | ||||
-rw-r--r-- | drivers/acpi/x86/apple.c | 141 |
4 files changed, 151 insertions, 0 deletions
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index b1aacfc62b1f..90265ab4437a 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile | |||
@@ -50,6 +50,7 @@ acpi-$(CONFIG_ACPI_REDUCED_HARDWARE_ONLY) += evged.o | |||
50 | acpi-y += sysfs.o | 50 | acpi-y += sysfs.o |
51 | acpi-y += property.o | 51 | acpi-y += property.o |
52 | acpi-$(CONFIG_X86) += acpi_cmos_rtc.o | 52 | acpi-$(CONFIG_X86) += acpi_cmos_rtc.o |
53 | acpi-$(CONFIG_X86) += x86/apple.o | ||
53 | acpi-$(CONFIG_X86) += x86/utils.o | 54 | acpi-$(CONFIG_X86) += x86/utils.o |
54 | acpi-$(CONFIG_DEBUG_FS) += debugfs.o | 55 | acpi-$(CONFIG_DEBUG_FS) += debugfs.o |
55 | acpi-$(CONFIG_ACPI_NUMA) += numa.o | 56 | acpi-$(CONFIG_ACPI_NUMA) += numa.o |
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 58dd7ab3c653..ee066d74f5bf 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h | |||
@@ -233,6 +233,12 @@ static inline void suspend_nvs_restore(void) {} | |||
233 | void acpi_init_properties(struct acpi_device *adev); | 233 | void acpi_init_properties(struct acpi_device *adev); |
234 | void acpi_free_properties(struct acpi_device *adev); | 234 | void acpi_free_properties(struct acpi_device *adev); |
235 | 235 | ||
236 | #ifdef CONFIG_X86 | ||
237 | void acpi_extract_apple_properties(struct acpi_device *adev); | ||
238 | #else | ||
239 | static inline void acpi_extract_apple_properties(struct acpi_device *adev) {} | ||
240 | #endif | ||
241 | |||
236 | /*-------------------------------------------------------------------------- | 242 | /*-------------------------------------------------------------------------- |
237 | Watchdog | 243 | Watchdog |
238 | -------------------------------------------------------------------------- */ | 244 | -------------------------------------------------------------------------- */ |
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 116bfc1937b5..fc1bb083f5f0 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c | |||
@@ -376,6 +376,9 @@ void acpi_init_properties(struct acpi_device *adev) | |||
376 | if (acpi_of && !adev->flags.of_compatible_ok) | 376 | if (acpi_of && !adev->flags.of_compatible_ok) |
377 | acpi_handle_info(adev->handle, | 377 | acpi_handle_info(adev->handle, |
378 | ACPI_DT_NAMESPACE_HID " requires 'compatible' property\n"); | 378 | ACPI_DT_NAMESPACE_HID " requires 'compatible' property\n"); |
379 | |||
380 | if (!adev->data.pointer) | ||
381 | acpi_extract_apple_properties(adev); | ||
379 | } | 382 | } |
380 | 383 | ||
381 | static void acpi_destroy_nondev_subnodes(struct list_head *list) | 384 | static void acpi_destroy_nondev_subnodes(struct list_head *list) |
diff --git a/drivers/acpi/x86/apple.c b/drivers/acpi/x86/apple.c new file mode 100644 index 000000000000..51b4cf9f25da --- /dev/null +++ b/drivers/acpi/x86/apple.c | |||
@@ -0,0 +1,141 @@ | |||
1 | /* | ||
2 | * apple.c - Apple ACPI quirks | ||
3 | * Copyright (C) 2017 Lukas Wunner <lukas@wunner.de> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License (version 2) as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | |||
10 | #include <linux/acpi.h> | ||
11 | #include <linux/bitmap.h> | ||
12 | #include <linux/platform_data/x86/apple.h> | ||
13 | #include <linux/uuid.h> | ||
14 | |||
15 | /* Apple _DSM device properties GUID */ | ||
16 | static const guid_t apple_prp_guid = | ||
17 | GUID_INIT(0xa0b5b7c6, 0x1318, 0x441c, | ||
18 | 0xb0, 0xc9, 0xfe, 0x69, 0x5e, 0xaf, 0x94, 0x9b); | ||
19 | |||
20 | /** | ||
21 | * acpi_extract_apple_properties - retrieve and convert Apple _DSM properties | ||
22 | * @adev: ACPI device for which to retrieve the properties | ||
23 | * | ||
24 | * Invoke Apple's custom _DSM once to check the protocol version and once more | ||
25 | * to retrieve the properties. They are marshalled up in a single package as | ||
26 | * alternating key/value elements, unlike _DSD which stores them as a package | ||
27 | * of 2-element packages. Convert to _DSD format and make them available under | ||
28 | * the primary fwnode. | ||
29 | */ | ||
30 | void acpi_extract_apple_properties(struct acpi_device *adev) | ||
31 | { | ||
32 | unsigned int i, j = 0, newsize = 0, numprops, numvalid; | ||
33 | union acpi_object *props, *newprops; | ||
34 | unsigned long *valid = NULL; | ||
35 | void *free_space; | ||
36 | |||
37 | if (!x86_apple_machine) | ||
38 | return; | ||
39 | |||
40 | props = acpi_evaluate_dsm_typed(adev->handle, &apple_prp_guid, 1, 0, | ||
41 | NULL, ACPI_TYPE_BUFFER); | ||
42 | if (!props) | ||
43 | return; | ||
44 | |||
45 | if (!props->buffer.length) | ||
46 | goto out_free; | ||
47 | |||
48 | if (props->buffer.pointer[0] != 3) { | ||
49 | acpi_handle_info(adev->handle, FW_INFO | ||
50 | "unsupported properties version %*ph\n", | ||
51 | props->buffer.length, props->buffer.pointer); | ||
52 | goto out_free; | ||
53 | } | ||
54 | |||
55 | ACPI_FREE(props); | ||
56 | props = acpi_evaluate_dsm_typed(adev->handle, &apple_prp_guid, 1, 1, | ||
57 | NULL, ACPI_TYPE_PACKAGE); | ||
58 | if (!props) | ||
59 | return; | ||
60 | |||
61 | numprops = props->package.count / 2; | ||
62 | if (!numprops) | ||
63 | goto out_free; | ||
64 | |||
65 | valid = kcalloc(BITS_TO_LONGS(numprops), sizeof(long), GFP_KERNEL); | ||
66 | if (!valid) | ||
67 | goto out_free; | ||
68 | |||
69 | /* newsize = key length + value length of each tuple */ | ||
70 | for (i = 0; i < numprops; i++) { | ||
71 | union acpi_object *key = &props->package.elements[i * 2]; | ||
72 | union acpi_object *val = &props->package.elements[i * 2 + 1]; | ||
73 | |||
74 | if ( key->type != ACPI_TYPE_STRING || | ||
75 | (val->type != ACPI_TYPE_INTEGER && | ||
76 | val->type != ACPI_TYPE_BUFFER)) | ||
77 | continue; /* skip invalid properties */ | ||
78 | |||
79 | __set_bit(i, valid); | ||
80 | newsize += key->string.length + 1; | ||
81 | if ( val->type == ACPI_TYPE_BUFFER) | ||
82 | newsize += val->buffer.length; | ||
83 | } | ||
84 | |||
85 | numvalid = bitmap_weight(valid, numprops); | ||
86 | if (numprops > numvalid) | ||
87 | acpi_handle_info(adev->handle, FW_INFO | ||
88 | "skipped %u properties: wrong type\n", | ||
89 | numprops - numvalid); | ||
90 | if (numvalid == 0) | ||
91 | goto out_free; | ||
92 | |||
93 | /* newsize += top-level package + 3 objects for each key/value tuple */ | ||
94 | newsize += (1 + 3 * numvalid) * sizeof(union acpi_object); | ||
95 | newprops = ACPI_ALLOCATE_ZEROED(newsize); | ||
96 | if (!newprops) | ||
97 | goto out_free; | ||
98 | |||
99 | /* layout: top-level package | packages | key/value tuples | strings */ | ||
100 | newprops->type = ACPI_TYPE_PACKAGE; | ||
101 | newprops->package.count = numvalid; | ||
102 | newprops->package.elements = &newprops[1]; | ||
103 | free_space = &newprops[1 + 3 * numvalid]; | ||
104 | |||
105 | for_each_set_bit(i, valid, numprops) { | ||
106 | union acpi_object *key = &props->package.elements[i * 2]; | ||
107 | union acpi_object *val = &props->package.elements[i * 2 + 1]; | ||
108 | unsigned int k = 1 + numvalid + j * 2; /* index into newprops */ | ||
109 | unsigned int v = k + 1; | ||
110 | |||
111 | newprops[1 + j].type = ACPI_TYPE_PACKAGE; | ||
112 | newprops[1 + j].package.count = 2; | ||
113 | newprops[1 + j].package.elements = &newprops[k]; | ||
114 | |||
115 | newprops[k].type = ACPI_TYPE_STRING; | ||
116 | newprops[k].string.length = key->string.length; | ||
117 | newprops[k].string.pointer = free_space; | ||
118 | memcpy(free_space, key->string.pointer, key->string.length); | ||
119 | free_space += key->string.length + 1; | ||
120 | |||
121 | newprops[v].type = val->type; | ||
122 | if (val->type == ACPI_TYPE_INTEGER) { | ||
123 | newprops[v].integer.value = val->integer.value; | ||
124 | } else { | ||
125 | newprops[v].buffer.length = val->buffer.length; | ||
126 | newprops[v].buffer.pointer = free_space; | ||
127 | memcpy(free_space, val->buffer.pointer, | ||
128 | val->buffer.length); | ||
129 | free_space += val->buffer.length; | ||
130 | } | ||
131 | j++; /* count valid properties */ | ||
132 | } | ||
133 | WARN_ON(free_space != (void *)newprops + newsize); | ||
134 | |||
135 | adev->data.properties = newprops; | ||
136 | adev->data.pointer = newprops; | ||
137 | |||
138 | out_free: | ||
139 | ACPI_FREE(props); | ||
140 | kfree(valid); | ||
141 | } | ||