Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/acpi/acpi_adxl.c
26278 views
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
194