162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * apple.c - Apple ACPI quirks
462306a36Sopenharmony_ci * Copyright (C) 2017 Lukas Wunner <lukas@wunner.de>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/acpi.h>
862306a36Sopenharmony_ci#include <linux/bitmap.h>
962306a36Sopenharmony_ci#include <linux/platform_data/x86/apple.h>
1062306a36Sopenharmony_ci#include <linux/uuid.h>
1162306a36Sopenharmony_ci#include "../internal.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/* Apple _DSM device properties GUID */
1462306a36Sopenharmony_cistatic const guid_t apple_prp_guid =
1562306a36Sopenharmony_ci	GUID_INIT(0xa0b5b7c6, 0x1318, 0x441c,
1662306a36Sopenharmony_ci		  0xb0, 0xc9, 0xfe, 0x69, 0x5e, 0xaf, 0x94, 0x9b);
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/**
1962306a36Sopenharmony_ci * acpi_extract_apple_properties - retrieve and convert Apple _DSM properties
2062306a36Sopenharmony_ci * @adev: ACPI device for which to retrieve the properties
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * Invoke Apple's custom _DSM once to check the protocol version and once more
2362306a36Sopenharmony_ci * to retrieve the properties.  They are marshalled up in a single package as
2462306a36Sopenharmony_ci * alternating key/value elements, unlike _DSD which stores them as a package
2562306a36Sopenharmony_ci * of 2-element packages.  Convert to _DSD format and make them available under
2662306a36Sopenharmony_ci * the primary fwnode.
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_civoid acpi_extract_apple_properties(struct acpi_device *adev)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	unsigned int i, j = 0, newsize = 0, numprops, numvalid;
3162306a36Sopenharmony_ci	union acpi_object *props, *newprops;
3262306a36Sopenharmony_ci	unsigned long *valid = NULL;
3362306a36Sopenharmony_ci	void *free_space;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	if (!x86_apple_machine)
3662306a36Sopenharmony_ci		return;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	props = acpi_evaluate_dsm_typed(adev->handle, &apple_prp_guid, 1, 0,
3962306a36Sopenharmony_ci					NULL, ACPI_TYPE_BUFFER);
4062306a36Sopenharmony_ci	if (!props)
4162306a36Sopenharmony_ci		return;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	if (!props->buffer.length)
4462306a36Sopenharmony_ci		goto out_free;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (props->buffer.pointer[0] != 3) {
4762306a36Sopenharmony_ci		acpi_handle_info(adev->handle, FW_INFO
4862306a36Sopenharmony_ci				 "unsupported properties version %*ph\n",
4962306a36Sopenharmony_ci				 props->buffer.length, props->buffer.pointer);
5062306a36Sopenharmony_ci		goto out_free;
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	ACPI_FREE(props);
5462306a36Sopenharmony_ci	props = acpi_evaluate_dsm_typed(adev->handle, &apple_prp_guid, 1, 1,
5562306a36Sopenharmony_ci					NULL, ACPI_TYPE_PACKAGE);
5662306a36Sopenharmony_ci	if (!props)
5762306a36Sopenharmony_ci		return;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	numprops = props->package.count / 2;
6062306a36Sopenharmony_ci	if (!numprops)
6162306a36Sopenharmony_ci		goto out_free;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	valid = bitmap_zalloc(numprops, GFP_KERNEL);
6462306a36Sopenharmony_ci	if (!valid)
6562306a36Sopenharmony_ci		goto out_free;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	/* newsize = key length + value length of each tuple */
6862306a36Sopenharmony_ci	for (i = 0; i < numprops; i++) {
6962306a36Sopenharmony_ci		union acpi_object *key = &props->package.elements[i * 2];
7062306a36Sopenharmony_ci		union acpi_object *val = &props->package.elements[i * 2 + 1];
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci		if ( key->type != ACPI_TYPE_STRING ||
7362306a36Sopenharmony_ci		    (val->type != ACPI_TYPE_INTEGER &&
7462306a36Sopenharmony_ci		     val->type != ACPI_TYPE_BUFFER &&
7562306a36Sopenharmony_ci		     val->type != ACPI_TYPE_STRING))
7662306a36Sopenharmony_ci			continue; /* skip invalid properties */
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci		__set_bit(i, valid);
7962306a36Sopenharmony_ci		newsize += key->string.length + 1;
8062306a36Sopenharmony_ci		if ( val->type == ACPI_TYPE_BUFFER)
8162306a36Sopenharmony_ci			newsize += val->buffer.length;
8262306a36Sopenharmony_ci		else if (val->type == ACPI_TYPE_STRING)
8362306a36Sopenharmony_ci			newsize += val->string.length + 1;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	numvalid = bitmap_weight(valid, numprops);
8762306a36Sopenharmony_ci	if (numprops > numvalid)
8862306a36Sopenharmony_ci		acpi_handle_info(adev->handle, FW_INFO
8962306a36Sopenharmony_ci				 "skipped %u properties: wrong type\n",
9062306a36Sopenharmony_ci				 numprops - numvalid);
9162306a36Sopenharmony_ci	if (numvalid == 0)
9262306a36Sopenharmony_ci		goto out_free;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* newsize += top-level package + 3 objects for each key/value tuple */
9562306a36Sopenharmony_ci	newsize	+= (1 + 3 * numvalid) * sizeof(union acpi_object);
9662306a36Sopenharmony_ci	newprops = ACPI_ALLOCATE_ZEROED(newsize);
9762306a36Sopenharmony_ci	if (!newprops)
9862306a36Sopenharmony_ci		goto out_free;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/* layout: top-level package | packages | key/value tuples | strings */
10162306a36Sopenharmony_ci	newprops->type = ACPI_TYPE_PACKAGE;
10262306a36Sopenharmony_ci	newprops->package.count = numvalid;
10362306a36Sopenharmony_ci	newprops->package.elements = &newprops[1];
10462306a36Sopenharmony_ci	free_space = &newprops[1 + 3 * numvalid];
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	for_each_set_bit(i, valid, numprops) {
10762306a36Sopenharmony_ci		union acpi_object *key = &props->package.elements[i * 2];
10862306a36Sopenharmony_ci		union acpi_object *val = &props->package.elements[i * 2 + 1];
10962306a36Sopenharmony_ci		unsigned int k = 1 + numvalid + j * 2; /* index into newprops */
11062306a36Sopenharmony_ci		unsigned int v = k + 1;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		newprops[1 + j].type = ACPI_TYPE_PACKAGE;
11362306a36Sopenharmony_ci		newprops[1 + j].package.count = 2;
11462306a36Sopenharmony_ci		newprops[1 + j].package.elements = &newprops[k];
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		newprops[k].type = ACPI_TYPE_STRING;
11762306a36Sopenharmony_ci		newprops[k].string.length = key->string.length;
11862306a36Sopenharmony_ci		newprops[k].string.pointer = free_space;
11962306a36Sopenharmony_ci		memcpy(free_space, key->string.pointer, key->string.length);
12062306a36Sopenharmony_ci		free_space += key->string.length + 1;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci		newprops[v].type = val->type;
12362306a36Sopenharmony_ci		if (val->type == ACPI_TYPE_INTEGER) {
12462306a36Sopenharmony_ci			newprops[v].integer.value = val->integer.value;
12562306a36Sopenharmony_ci		} else if (val->type == ACPI_TYPE_STRING) {
12662306a36Sopenharmony_ci			newprops[v].string.length = val->string.length;
12762306a36Sopenharmony_ci			newprops[v].string.pointer = free_space;
12862306a36Sopenharmony_ci			memcpy(free_space, val->string.pointer,
12962306a36Sopenharmony_ci			       val->string.length);
13062306a36Sopenharmony_ci			free_space += val->string.length + 1;
13162306a36Sopenharmony_ci		} else {
13262306a36Sopenharmony_ci			newprops[v].buffer.length = val->buffer.length;
13362306a36Sopenharmony_ci			newprops[v].buffer.pointer = free_space;
13462306a36Sopenharmony_ci			memcpy(free_space, val->buffer.pointer,
13562306a36Sopenharmony_ci			       val->buffer.length);
13662306a36Sopenharmony_ci			free_space += val->buffer.length;
13762306a36Sopenharmony_ci		}
13862306a36Sopenharmony_ci		j++; /* count valid properties */
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci	WARN_ON(free_space != (void *)newprops + newsize);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	adev->data.pointer = newprops;
14362306a36Sopenharmony_ci	acpi_data_add_props(&adev->data, &apple_prp_guid, newprops);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ciout_free:
14662306a36Sopenharmony_ci	ACPI_FREE(props);
14762306a36Sopenharmony_ci	bitmap_free(valid);
14862306a36Sopenharmony_ci}
149