1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61#include "iwl-drv.h"
62#include "iwl-debug.h"
63#include "acpi.h"
64
65void *iwl_acpi_get_object(struct device *dev, acpi_string method)
66{
67 acpi_handle root_handle;
68 acpi_handle handle;
69 struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
70 acpi_status status;
71
72 root_handle = ACPI_HANDLE(dev);
73 if (!root_handle) {
74 IWL_DEBUG_DEV_RADIO(dev,
75 "Could not retrieve root port ACPI handle\n");
76 return ERR_PTR(-ENOENT);
77 }
78
79
80 status = acpi_get_handle(root_handle, method, &handle);
81 if (ACPI_FAILURE(status)) {
82 IWL_DEBUG_DEV_RADIO(dev, "%s method not found\n", method);
83 return ERR_PTR(-ENOENT);
84 }
85
86
87 status = acpi_evaluate_object(handle, NULL, NULL, &buf);
88 if (ACPI_FAILURE(status)) {
89 IWL_DEBUG_DEV_RADIO(dev, "%s invocation failed (0x%x)\n",
90 method, status);
91 return ERR_PTR(-ENOENT);
92 }
93
94 return buf.pointer;
95}
96IWL_EXPORT_SYMBOL(iwl_acpi_get_object);
97
98union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev,
99 union acpi_object *data,
100 int data_size)
101{
102 int i;
103 union acpi_object *wifi_pkg;
104
105
106
107
108
109
110 if (WARN_ON_ONCE(data_size < 2))
111 return ERR_PTR(-EINVAL);
112
113
114
115
116
117
118 if (data->type != ACPI_TYPE_PACKAGE ||
119 data->package.count < 2 ||
120 data->package.elements[0].type != ACPI_TYPE_INTEGER ||
121 data->package.elements[0].integer.value != 0) {
122 IWL_DEBUG_DEV_RADIO(dev, "Unsupported packages structure\n");
123 return ERR_PTR(-EINVAL);
124 }
125
126
127 for (i = 1; i < data->package.count; i++) {
128 union acpi_object *domain;
129
130 wifi_pkg = &data->package.elements[i];
131
132
133 if (wifi_pkg->type != ACPI_TYPE_PACKAGE ||
134 wifi_pkg->package.count != data_size)
135 continue;
136
137 domain = &wifi_pkg->package.elements[0];
138 if (domain->type == ACPI_TYPE_INTEGER &&
139 domain->integer.value == ACPI_WIFI_DOMAIN)
140 goto found;
141 }
142
143 return ERR_PTR(-ENOENT);
144
145found:
146 return wifi_pkg;
147}
148IWL_EXPORT_SYMBOL(iwl_acpi_get_wifi_pkg);
149
150int iwl_acpi_get_mcc(struct device *dev, char *mcc)
151{
152 union acpi_object *wifi_pkg, *data;
153 u32 mcc_val;
154 int ret;
155
156 data = iwl_acpi_get_object(dev, ACPI_WRDD_METHOD);
157 if (IS_ERR(data))
158 return PTR_ERR(data);
159
160 wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE);
161 if (IS_ERR(wifi_pkg)) {
162 ret = PTR_ERR(wifi_pkg);
163 goto out_free;
164 }
165
166 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
167 ret = -EINVAL;
168 goto out_free;
169 }
170
171 mcc_val = wifi_pkg->package.elements[1].integer.value;
172
173 mcc[0] = (mcc_val >> 8) & 0xff;
174 mcc[1] = mcc_val & 0xff;
175 mcc[2] = '\0';
176
177 ret = 0;
178out_free:
179 kfree(data);
180 return ret;
181}
182IWL_EXPORT_SYMBOL(iwl_acpi_get_mcc);
183
184u64 iwl_acpi_get_pwr_limit(struct device *dev)
185{
186 union acpi_object *data, *wifi_pkg;
187 u64 dflt_pwr_limit;
188
189 data = iwl_acpi_get_object(dev, ACPI_SPLC_METHOD);
190 if (IS_ERR(data)) {
191 dflt_pwr_limit = 0;
192 goto out;
193 }
194
195 wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data,
196 ACPI_SPLC_WIFI_DATA_SIZE);
197 if (IS_ERR(wifi_pkg) ||
198 wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) {
199 dflt_pwr_limit = 0;
200 goto out_free;
201 }
202
203 dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value;
204out_free:
205 kfree(data);
206out:
207 return dflt_pwr_limit;
208}
209IWL_EXPORT_SYMBOL(iwl_acpi_get_pwr_limit);
210
211int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk)
212{
213 union acpi_object *wifi_pkg, *data;
214 int ret;
215
216 data = iwl_acpi_get_object(dev, ACPI_ECKV_METHOD);
217 if (IS_ERR(data))
218 return PTR_ERR(data);
219
220 wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_ECKV_WIFI_DATA_SIZE);
221 if (IS_ERR(wifi_pkg)) {
222 ret = PTR_ERR(wifi_pkg);
223 goto out_free;
224 }
225
226 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
227 ret = -EINVAL;
228 goto out_free;
229 }
230
231 *extl_clk = wifi_pkg->package.elements[1].integer.value;
232
233 ret = 0;
234
235out_free:
236 kfree(data);
237 return ret;
238}
239IWL_EXPORT_SYMBOL(iwl_acpi_get_eckv);
240