Merge branch 'remotes/lorenzo/pci/imx'
[linux-2.6-microblaze.git] / drivers / acpi / acpi_adxl.c
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);