1 /******************************************************************************
2 *
3 * This file is provided under a dual BSD/GPLv2 license. When using or
4 * redistributing this file, you may do so under either license.
5 *
6 * GPL LICENSE SUMMARY
7 *
8 * Copyright(c) 2017 Intel Deutschland GmbH
9 * Copyright (C) 2019 Intel Corporation
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of version 2 of the GNU General Public License as
13 * published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * The full GNU General Public License is included in this distribution
21 * in the file called COPYING.
22 *
23 * Contact Information:
24 * Intel Linux Wireless <linuxwifi@intel.com>
25 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
26 *
27 * BSD LICENSE
28 *
29 * Copyright(c) 2017 Intel Deutschland GmbH
30 * Copyright (C) 2019 Intel Corporation
31 * All rights reserved.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 *
37 * * Redistributions of source code must retain the above copyright
38 * notice, this list of conditions and the following disclaimer.
39 * * Redistributions in binary form must reproduce the above copyright
40 * notice, this list of conditions and the following disclaimer in
41 * the documentation and/or other materials provided with the
42 * distribution.
43 * * Neither the name Intel Corporation nor the names of its
44 * contributors may be used to endorse or promote products derived
45 * from this software without specific prior written permission.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
48 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
49 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
50 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
51 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
52 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
53 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
54 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
55 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
56 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
57 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
58 *
59 *****************************************************************************/
60
61 #include "iwl-drv.h"
62 #include "iwl-debug.h"
63 #include "acpi.h"
64
iwl_acpi_get_object(struct device * dev,acpi_string method)65 void *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 /* Get the method's handle */
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 /* Call the method with no arguments */
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 }
96 IWL_EXPORT_SYMBOL(iwl_acpi_get_object);
97
iwl_acpi_get_wifi_pkg(struct device * dev,union acpi_object * data,int data_size,int * tbl_rev)98 union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev,
99 union acpi_object *data,
100 int data_size, int *tbl_rev)
101 {
102 int i;
103 union acpi_object *wifi_pkg;
104
105 /*
106 * We need at least one entry in the wifi package that
107 * describes the domain, and one more entry, otherwise there's
108 * no point in reading it.
109 */
110 if (WARN_ON_ONCE(data_size < 2))
111 return ERR_PTR(-EINVAL);
112
113 /*
114 * We need at least two packages, one for the revision and one
115 * for the data itself. Also check that the revision is valid
116 * (i.e. it is an integer smaller than 2, as we currently support only
117 * 2 revisions).
118 */
119 if (data->type != ACPI_TYPE_PACKAGE ||
120 data->package.count < 2 ||
121 data->package.elements[0].type != ACPI_TYPE_INTEGER ||
122 data->package.elements[0].integer.value > 1) {
123 IWL_DEBUG_DEV_RADIO(dev, "Unsupported packages structure\n");
124 return ERR_PTR(-EINVAL);
125 }
126
127 *tbl_rev = data->package.elements[0].integer.value;
128
129 /* loop through all the packages to find the one for WiFi */
130 for (i = 1; i < data->package.count; i++) {
131 union acpi_object *domain;
132
133 wifi_pkg = &data->package.elements[i];
134
135 /* skip entries that are not a package with the right size */
136 if (wifi_pkg->type != ACPI_TYPE_PACKAGE ||
137 wifi_pkg->package.count != data_size)
138 continue;
139
140 domain = &wifi_pkg->package.elements[0];
141 if (domain->type == ACPI_TYPE_INTEGER &&
142 domain->integer.value == ACPI_WIFI_DOMAIN)
143 goto found;
144 }
145
146 return ERR_PTR(-ENOENT);
147
148 found:
149 return wifi_pkg;
150 }
151 IWL_EXPORT_SYMBOL(iwl_acpi_get_wifi_pkg);
152
iwl_acpi_get_mcc(struct device * dev,char * mcc)153 int iwl_acpi_get_mcc(struct device *dev, char *mcc)
154 {
155 union acpi_object *wifi_pkg, *data;
156 u32 mcc_val;
157 int ret, tbl_rev;
158
159 data = iwl_acpi_get_object(dev, ACPI_WRDD_METHOD);
160 if (IS_ERR(data))
161 return PTR_ERR(data);
162
163 wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE,
164 &tbl_rev);
165 if (IS_ERR(wifi_pkg)) {
166 ret = PTR_ERR(wifi_pkg);
167 goto out_free;
168 }
169
170 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
171 tbl_rev != 0) {
172 ret = -EINVAL;
173 goto out_free;
174 }
175
176 mcc_val = wifi_pkg->package.elements[1].integer.value;
177
178 mcc[0] = (mcc_val >> 8) & 0xff;
179 mcc[1] = mcc_val & 0xff;
180 mcc[2] = '\0';
181
182 ret = 0;
183 out_free:
184 kfree(data);
185 return ret;
186 }
187 IWL_EXPORT_SYMBOL(iwl_acpi_get_mcc);
188
iwl_acpi_get_pwr_limit(struct device * dev)189 u64 iwl_acpi_get_pwr_limit(struct device *dev)
190 {
191 union acpi_object *data, *wifi_pkg;
192 u64 dflt_pwr_limit;
193 int tbl_rev;
194
195 data = iwl_acpi_get_object(dev, ACPI_SPLC_METHOD);
196 if (IS_ERR(data)) {
197 dflt_pwr_limit = 0;
198 goto out;
199 }
200
201 wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data,
202 ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev);
203 if (IS_ERR(wifi_pkg) || tbl_rev != 0 ||
204 wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) {
205 dflt_pwr_limit = 0;
206 goto out_free;
207 }
208
209 dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value;
210 out_free:
211 kfree(data);
212 out:
213 return dflt_pwr_limit;
214 }
215 IWL_EXPORT_SYMBOL(iwl_acpi_get_pwr_limit);
216
iwl_acpi_get_eckv(struct device * dev,u32 * extl_clk)217 int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk)
218 {
219 union acpi_object *wifi_pkg, *data;
220 int ret, tbl_rev;
221
222 data = iwl_acpi_get_object(dev, ACPI_ECKV_METHOD);
223 if (IS_ERR(data))
224 return PTR_ERR(data);
225
226 wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_ECKV_WIFI_DATA_SIZE,
227 &tbl_rev);
228 if (IS_ERR(wifi_pkg)) {
229 ret = PTR_ERR(wifi_pkg);
230 goto out_free;
231 }
232
233 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
234 tbl_rev != 0) {
235 ret = -EINVAL;
236 goto out_free;
237 }
238
239 *extl_clk = wifi_pkg->package.elements[1].integer.value;
240
241 ret = 0;
242
243 out_free:
244 kfree(data);
245 return ret;
246 }
247 IWL_EXPORT_SYMBOL(iwl_acpi_get_eckv);
248