xref: /linux/drivers/acpi/acpi_adxl.c (revision 24bce201d79807b668bf9d9e0aca801c5c0d5f78)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Address translation interface via ACPI DSM.
4  * Copyright (C) 2018 Intel Corporation
5  *
6  * Specification for this interface is available at:
7  *
8  *	https://cdrdv2.intel.com/v1/dl/getContent/603354
9  */
10 
11 #include <linux/acpi.h>
12 #include <linux/adxl.h>
13 
14 #define ADXL_REVISION			0x1
15 #define ADXL_IDX_GET_ADDR_PARAMS	0x1
16 #define ADXL_IDX_FORWARD_TRANSLATE	0x2
17 #define ACPI_ADXL_PATH			"\\_SB.ADXL"
18 
19 /*
20  * The specification doesn't provide a limit on how many
21  * components are in a memory address. But since we allocate
22  * memory based on the number the BIOS tells us, we should
23  * defend against insane values.
24  */
25 #define ADXL_MAX_COMPONENTS		500
26 
27 #undef pr_fmt
28 #define pr_fmt(fmt) "ADXL: " fmt
29 
30 static acpi_handle handle;
31 static union acpi_object *params;
32 static const guid_t adxl_guid =
33 	GUID_INIT(0xAA3C050A, 0x7EA4, 0x4C1F,
34 		  0xAF, 0xDA, 0x12, 0x67, 0xDF, 0xD3, 0xD4, 0x8D);
35 
36 static int adxl_count;
37 static char **adxl_component_names;
38 
39 static union acpi_object *adxl_dsm(int cmd, union acpi_object argv[])
40 {
41 	union acpi_object *obj, *o;
42 
43 	obj = acpi_evaluate_dsm_typed(handle, &adxl_guid, ADXL_REVISION,
44 				      cmd, argv, ACPI_TYPE_PACKAGE);
45 	if (!obj) {
46 		pr_info("DSM call failed for cmd=%d\n", cmd);
47 		return NULL;
48 	}
49 
50 	if (obj->package.count != 2) {
51 		pr_info("Bad pkg count %d\n", obj->package.count);
52 		goto err;
53 	}
54 
55 	o = obj->package.elements;
56 	if (o->type != ACPI_TYPE_INTEGER) {
57 		pr_info("Bad 1st element type %d\n", o->type);
58 		goto err;
59 	}
60 	if (o->integer.value) {
61 		pr_info("Bad ret val %llu\n", o->integer.value);
62 		goto err;
63 	}
64 
65 	o = obj->package.elements + 1;
66 	if (o->type != ACPI_TYPE_PACKAGE) {
67 		pr_info("Bad 2nd element type %d\n", o->type);
68 		goto err;
69 	}
70 	return obj;
71 
72 err:
73 	ACPI_FREE(obj);
74 	return NULL;
75 }
76 
77 /**
78  * adxl_get_component_names - get list of memory component names
79  * Returns NULL terminated list of string names
80  *
81  * Give the caller a pointer to the list of memory component names
82  * e.g. { "SystemAddress", "ProcessorSocketId", "ChannelId", ... NULL }
83  * Caller should count how many strings in order to allocate a buffer
84  * for the return from adxl_decode().
85  */
86 const char * const *adxl_get_component_names(void)
87 {
88 	return (const char * const *)adxl_component_names;
89 }
90 EXPORT_SYMBOL_GPL(adxl_get_component_names);
91 
92 /**
93  * adxl_decode - ask BIOS to decode a system address to memory address
94  * @addr: the address to decode
95  * @component_values: pointer to array of values for each component
96  * Returns 0 on success, negative error code otherwise
97  *
98  * The index of each value returned in the array matches the index of
99  * each component name returned by adxl_get_component_names().
100  * Components that are not defined for this address translation (e.g.
101  * mirror channel number for a non-mirrored address) are set to ~0ull.
102  */
103 int adxl_decode(u64 addr, u64 component_values[])
104 {
105 	union acpi_object argv4[2], *results, *r;
106 	int i, cnt;
107 
108 	if (!adxl_component_names)
109 		return -EOPNOTSUPP;
110 
111 	argv4[0].type = ACPI_TYPE_PACKAGE;
112 	argv4[0].package.count = 1;
113 	argv4[0].package.elements = &argv4[1];
114 	argv4[1].integer.type = ACPI_TYPE_INTEGER;
115 	argv4[1].integer.value = addr;
116 
117 	results = adxl_dsm(ADXL_IDX_FORWARD_TRANSLATE, argv4);
118 	if (!results)
119 		return -EINVAL;
120 
121 	r = results->package.elements + 1;
122 	cnt = r->package.count;
123 	if (cnt != adxl_count) {
124 		ACPI_FREE(results);
125 		return -EINVAL;
126 	}
127 	r = r->package.elements;
128 
129 	for (i = 0; i < cnt; i++)
130 		component_values[i] = r[i].integer.value;
131 
132 	ACPI_FREE(results);
133 
134 	return 0;
135 }
136 EXPORT_SYMBOL_GPL(adxl_decode);
137 
138 static int __init adxl_init(void)
139 {
140 	char *path = ACPI_ADXL_PATH;
141 	union acpi_object *p;
142 	acpi_status status;
143 	int i;
144 
145 	status = acpi_get_handle(NULL, path, &handle);
146 	if (ACPI_FAILURE(status)) {
147 		pr_debug("No ACPI handle for path %s\n", path);
148 		return -ENODEV;
149 	}
150 
151 	if (!acpi_has_method(handle, "_DSM")) {
152 		pr_info("No DSM method\n");
153 		return -ENODEV;
154 	}
155 
156 	if (!acpi_check_dsm(handle, &adxl_guid, ADXL_REVISION,
157 			    ADXL_IDX_GET_ADDR_PARAMS |
158 			    ADXL_IDX_FORWARD_TRANSLATE)) {
159 		pr_info("DSM method does not support forward translate\n");
160 		return -ENODEV;
161 	}
162 
163 	params = adxl_dsm(ADXL_IDX_GET_ADDR_PARAMS, NULL);
164 	if (!params) {
165 		pr_info("Failed to get component names\n");
166 		return -ENODEV;
167 	}
168 
169 	p = params->package.elements + 1;
170 	adxl_count = p->package.count;
171 	if (adxl_count > ADXL_MAX_COMPONENTS) {
172 		pr_info("Insane number of address component names %d\n", adxl_count);
173 		ACPI_FREE(params);
174 		return -ENODEV;
175 	}
176 	p = p->package.elements;
177 
178 	/*
179 	 * Allocate one extra for NULL termination.
180 	 */
181 	adxl_component_names = kcalloc(adxl_count + 1, sizeof(char *), GFP_KERNEL);
182 	if (!adxl_component_names) {
183 		ACPI_FREE(params);
184 		return -ENOMEM;
185 	}
186 
187 	for (i = 0; i < adxl_count; i++)
188 		adxl_component_names[i] = p[i].string.pointer;
189 
190 	return 0;
191 }
192 subsys_initcall(adxl_init);
193