18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * apple.c - Apple ACPI quirks 48c2ecf20Sopenharmony_ci * Copyright (C) 2017 Lukas Wunner <lukas@wunner.de> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/acpi.h> 88c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 98c2ecf20Sopenharmony_ci#include <linux/platform_data/x86/apple.h> 108c2ecf20Sopenharmony_ci#include <linux/uuid.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* Apple _DSM device properties GUID */ 138c2ecf20Sopenharmony_cistatic const guid_t apple_prp_guid = 148c2ecf20Sopenharmony_ci GUID_INIT(0xa0b5b7c6, 0x1318, 0x441c, 158c2ecf20Sopenharmony_ci 0xb0, 0xc9, 0xfe, 0x69, 0x5e, 0xaf, 0x94, 0x9b); 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/** 188c2ecf20Sopenharmony_ci * acpi_extract_apple_properties - retrieve and convert Apple _DSM properties 198c2ecf20Sopenharmony_ci * @adev: ACPI device for which to retrieve the properties 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * Invoke Apple's custom _DSM once to check the protocol version and once more 228c2ecf20Sopenharmony_ci * to retrieve the properties. They are marshalled up in a single package as 238c2ecf20Sopenharmony_ci * alternating key/value elements, unlike _DSD which stores them as a package 248c2ecf20Sopenharmony_ci * of 2-element packages. Convert to _DSD format and make them available under 258c2ecf20Sopenharmony_ci * the primary fwnode. 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_civoid acpi_extract_apple_properties(struct acpi_device *adev) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci unsigned int i, j = 0, newsize = 0, numprops, numvalid; 308c2ecf20Sopenharmony_ci union acpi_object *props, *newprops; 318c2ecf20Sopenharmony_ci unsigned long *valid = NULL; 328c2ecf20Sopenharmony_ci void *free_space; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (!x86_apple_machine) 358c2ecf20Sopenharmony_ci return; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci props = acpi_evaluate_dsm_typed(adev->handle, &apple_prp_guid, 1, 0, 388c2ecf20Sopenharmony_ci NULL, ACPI_TYPE_BUFFER); 398c2ecf20Sopenharmony_ci if (!props) 408c2ecf20Sopenharmony_ci return; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (!props->buffer.length) 438c2ecf20Sopenharmony_ci goto out_free; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (props->buffer.pointer[0] != 3) { 468c2ecf20Sopenharmony_ci acpi_handle_info(adev->handle, FW_INFO 478c2ecf20Sopenharmony_ci "unsupported properties version %*ph\n", 488c2ecf20Sopenharmony_ci props->buffer.length, props->buffer.pointer); 498c2ecf20Sopenharmony_ci goto out_free; 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci ACPI_FREE(props); 538c2ecf20Sopenharmony_ci props = acpi_evaluate_dsm_typed(adev->handle, &apple_prp_guid, 1, 1, 548c2ecf20Sopenharmony_ci NULL, ACPI_TYPE_PACKAGE); 558c2ecf20Sopenharmony_ci if (!props) 568c2ecf20Sopenharmony_ci return; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci numprops = props->package.count / 2; 598c2ecf20Sopenharmony_ci if (!numprops) 608c2ecf20Sopenharmony_ci goto out_free; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci valid = bitmap_zalloc(numprops, GFP_KERNEL); 638c2ecf20Sopenharmony_ci if (!valid) 648c2ecf20Sopenharmony_ci goto out_free; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* newsize = key length + value length of each tuple */ 678c2ecf20Sopenharmony_ci for (i = 0; i < numprops; i++) { 688c2ecf20Sopenharmony_ci union acpi_object *key = &props->package.elements[i * 2]; 698c2ecf20Sopenharmony_ci union acpi_object *val = &props->package.elements[i * 2 + 1]; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if ( key->type != ACPI_TYPE_STRING || 728c2ecf20Sopenharmony_ci (val->type != ACPI_TYPE_INTEGER && 738c2ecf20Sopenharmony_ci val->type != ACPI_TYPE_BUFFER)) 748c2ecf20Sopenharmony_ci continue; /* skip invalid properties */ 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci __set_bit(i, valid); 778c2ecf20Sopenharmony_ci newsize += key->string.length + 1; 788c2ecf20Sopenharmony_ci if ( val->type == ACPI_TYPE_BUFFER) 798c2ecf20Sopenharmony_ci newsize += val->buffer.length; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci numvalid = bitmap_weight(valid, numprops); 838c2ecf20Sopenharmony_ci if (numprops > numvalid) 848c2ecf20Sopenharmony_ci acpi_handle_info(adev->handle, FW_INFO 858c2ecf20Sopenharmony_ci "skipped %u properties: wrong type\n", 868c2ecf20Sopenharmony_ci numprops - numvalid); 878c2ecf20Sopenharmony_ci if (numvalid == 0) 888c2ecf20Sopenharmony_ci goto out_free; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* newsize += top-level package + 3 objects for each key/value tuple */ 918c2ecf20Sopenharmony_ci newsize += (1 + 3 * numvalid) * sizeof(union acpi_object); 928c2ecf20Sopenharmony_ci newprops = ACPI_ALLOCATE_ZEROED(newsize); 938c2ecf20Sopenharmony_ci if (!newprops) 948c2ecf20Sopenharmony_ci goto out_free; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* layout: top-level package | packages | key/value tuples | strings */ 978c2ecf20Sopenharmony_ci newprops->type = ACPI_TYPE_PACKAGE; 988c2ecf20Sopenharmony_ci newprops->package.count = numvalid; 998c2ecf20Sopenharmony_ci newprops->package.elements = &newprops[1]; 1008c2ecf20Sopenharmony_ci free_space = &newprops[1 + 3 * numvalid]; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci for_each_set_bit(i, valid, numprops) { 1038c2ecf20Sopenharmony_ci union acpi_object *key = &props->package.elements[i * 2]; 1048c2ecf20Sopenharmony_ci union acpi_object *val = &props->package.elements[i * 2 + 1]; 1058c2ecf20Sopenharmony_ci unsigned int k = 1 + numvalid + j * 2; /* index into newprops */ 1068c2ecf20Sopenharmony_ci unsigned int v = k + 1; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci newprops[1 + j].type = ACPI_TYPE_PACKAGE; 1098c2ecf20Sopenharmony_ci newprops[1 + j].package.count = 2; 1108c2ecf20Sopenharmony_ci newprops[1 + j].package.elements = &newprops[k]; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci newprops[k].type = ACPI_TYPE_STRING; 1138c2ecf20Sopenharmony_ci newprops[k].string.length = key->string.length; 1148c2ecf20Sopenharmony_ci newprops[k].string.pointer = free_space; 1158c2ecf20Sopenharmony_ci memcpy(free_space, key->string.pointer, key->string.length); 1168c2ecf20Sopenharmony_ci free_space += key->string.length + 1; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci newprops[v].type = val->type; 1198c2ecf20Sopenharmony_ci if (val->type == ACPI_TYPE_INTEGER) { 1208c2ecf20Sopenharmony_ci newprops[v].integer.value = val->integer.value; 1218c2ecf20Sopenharmony_ci } else { 1228c2ecf20Sopenharmony_ci newprops[v].buffer.length = val->buffer.length; 1238c2ecf20Sopenharmony_ci newprops[v].buffer.pointer = free_space; 1248c2ecf20Sopenharmony_ci memcpy(free_space, val->buffer.pointer, 1258c2ecf20Sopenharmony_ci val->buffer.length); 1268c2ecf20Sopenharmony_ci free_space += val->buffer.length; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci j++; /* count valid properties */ 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci WARN_ON(free_space != (void *)newprops + newsize); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci adev->data.pointer = newprops; 1338c2ecf20Sopenharmony_ci acpi_data_add_props(&adev->data, &apple_prp_guid, newprops); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ciout_free: 1368c2ecf20Sopenharmony_ci ACPI_FREE(props); 1378c2ecf20Sopenharmony_ci bitmap_free(valid); 1388c2ecf20Sopenharmony_ci} 139