18e99ea8dSJohannes Berg // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
28e99ea8dSJohannes Berg /*
38e99ea8dSJohannes Berg * Copyright (C) 2017 Intel Deutschland GmbH
409396a4fSJohannes Berg * Copyright (C) 2019-2023 Intel Corporation
58e99ea8dSJohannes Berg */
69db93491SGil Adam #include <linux/uuid.h>
7e8e10a37SMatt Chen #include <linux/dmi.h>
8813df5ceSLuca Coelho #include "iwl-drv.h"
9813df5ceSLuca Coelho #include "iwl-debug.h"
10813df5ceSLuca Coelho #include "acpi.h"
1139c1a972SIhab Zhaika #include "fw/runtime.h"
12813df5ceSLuca Coelho
134e8fe214SGregory Greenman const guid_t iwl_guid = GUID_INIT(0xF21202BF, 0x8F78, 0x4DC6,
149db93491SGil Adam 0xA5, 0xB3, 0x1F, 0x73,
159db93491SGil Adam 0x8E, 0x28, 0x5A, 0xDE);
164e8fe214SGregory Greenman IWL_EXPORT_SYMBOL(iwl_guid);
174e8fe214SGregory Greenman
184e8fe214SGregory Greenman const guid_t iwl_rfi_guid = GUID_INIT(0x7266172C, 0x220B, 0x4B29,
194e8fe214SGregory Greenman 0x81, 0x4F, 0x75, 0xE4,
204e8fe214SGregory Greenman 0xDD, 0x26, 0xB5, 0xFD);
214e8fe214SGregory Greenman IWL_EXPORT_SYMBOL(iwl_rfi_guid);
229db93491SGil Adam
23e8e10a37SMatt Chen static const struct dmi_system_id dmi_ppag_approved_list[] = {
24e8e10a37SMatt Chen { .ident = "HP",
25e8e10a37SMatt Chen .matches = {
26e8e10a37SMatt Chen DMI_MATCH(DMI_SYS_VENDOR, "HP"),
27e8e10a37SMatt Chen },
28e8e10a37SMatt Chen },
29e8e10a37SMatt Chen { .ident = "SAMSUNG",
30e8e10a37SMatt Chen .matches = {
31e8e10a37SMatt Chen DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"),
32e8e10a37SMatt Chen },
33e8e10a37SMatt Chen },
34e8e10a37SMatt Chen { .ident = "MSFT",
35e8e10a37SMatt Chen .matches = {
36e8e10a37SMatt Chen DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
37e8e10a37SMatt Chen },
38e8e10a37SMatt Chen },
39e8e10a37SMatt Chen { .ident = "ASUS",
40e8e10a37SMatt Chen .matches = {
41eca7296dSAlon Giladi DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
42e8e10a37SMatt Chen },
43e8e10a37SMatt Chen },
4458e68276SAriel Malamud { .ident = "GOOGLE-HP",
4558e68276SAriel Malamud .matches = {
4658e68276SAriel Malamud DMI_MATCH(DMI_SYS_VENDOR, "Google"),
4758e68276SAriel Malamud DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
4858e68276SAriel Malamud },
4958e68276SAriel Malamud },
507bc57ca9SGolan Ben Ami { .ident = "GOOGLE-ASUS",
517bc57ca9SGolan Ben Ami .matches = {
527bc57ca9SGolan Ben Ami DMI_MATCH(DMI_SYS_VENDOR, "Google"),
537bc57ca9SGolan Ben Ami DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTek COMPUTER INC."),
547bc57ca9SGolan Ben Ami },
557bc57ca9SGolan Ben Ami },
567bc57ca9SGolan Ben Ami { .ident = "GOOGLE-SAMSUNG",
577bc57ca9SGolan Ben Ami .matches = {
587bc57ca9SGolan Ben Ami DMI_MATCH(DMI_SYS_VENDOR, "Google"),
597bc57ca9SGolan Ben Ami DMI_MATCH(DMI_BOARD_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"),
607bc57ca9SGolan Ben Ami },
617bc57ca9SGolan Ben Ami },
629e694212SAlon Giladi { .ident = "DELL",
639e694212SAlon Giladi .matches = {
649e694212SAlon Giladi DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
659e694212SAlon Giladi },
669e694212SAlon Giladi },
679e694212SAlon Giladi { .ident = "DELL",
689e694212SAlon Giladi .matches = {
699e694212SAlon Giladi DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
709e694212SAlon Giladi },
719e694212SAlon Giladi },
72533d9308SGregory Greenman { .ident = "RAZER",
73533d9308SGregory Greenman .matches = {
74533d9308SGregory Greenman DMI_MATCH(DMI_SYS_VENDOR, "Razer"),
75533d9308SGregory Greenman },
76533d9308SGregory Greenman },
77e8e10a37SMatt Chen {}
78e8e10a37SMatt Chen };
79e8e10a37SMatt Chen
iwl_acpi_get_handle(struct device * dev,acpi_string method,acpi_handle * ret_handle)809db93491SGil Adam static int iwl_acpi_get_handle(struct device *dev, acpi_string method,
819db93491SGil Adam acpi_handle *ret_handle)
82813df5ceSLuca Coelho {
83813df5ceSLuca Coelho acpi_handle root_handle;
84813df5ceSLuca Coelho acpi_status status;
85813df5ceSLuca Coelho
86813df5ceSLuca Coelho root_handle = ACPI_HANDLE(dev);
87813df5ceSLuca Coelho if (!root_handle) {
88813df5ceSLuca Coelho IWL_DEBUG_DEV_RADIO(dev,
899db93491SGil Adam "ACPI: Could not retrieve root port handle\n");
909db93491SGil Adam return -ENOENT;
91813df5ceSLuca Coelho }
92813df5ceSLuca Coelho
939db93491SGil Adam status = acpi_get_handle(root_handle, method, ret_handle);
94813df5ceSLuca Coelho if (ACPI_FAILURE(status)) {
959db93491SGil Adam IWL_DEBUG_DEV_RADIO(dev,
969db93491SGil Adam "ACPI: %s method not found\n", method);
979db93491SGil Adam return -ENOENT;
98813df5ceSLuca Coelho }
999db93491SGil Adam return 0;
1009db93491SGil Adam }
1019db93491SGil Adam
iwl_acpi_get_object(struct device * dev,acpi_string method)10209396a4fSJohannes Berg static void *iwl_acpi_get_object(struct device *dev, acpi_string method)
1039db93491SGil Adam {
1049db93491SGil Adam struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
1059db93491SGil Adam acpi_handle handle;
1069db93491SGil Adam acpi_status status;
1079db93491SGil Adam int ret;
1089db93491SGil Adam
1099db93491SGil Adam ret = iwl_acpi_get_handle(dev, method, &handle);
1109db93491SGil Adam if (ret)
1119db93491SGil Adam return ERR_PTR(-ENOENT);
112813df5ceSLuca Coelho
113813df5ceSLuca Coelho /* Call the method with no arguments */
114813df5ceSLuca Coelho status = acpi_evaluate_object(handle, NULL, NULL, &buf);
115813df5ceSLuca Coelho if (ACPI_FAILURE(status)) {
1169db93491SGil Adam IWL_DEBUG_DEV_RADIO(dev,
1179db93491SGil Adam "ACPI: %s method invocation failed (status: 0x%x)\n",
118813df5ceSLuca Coelho method, status);
119813df5ceSLuca Coelho return ERR_PTR(-ENOENT);
120813df5ceSLuca Coelho }
121813df5ceSLuca Coelho return buf.pointer;
122813df5ceSLuca Coelho }
1232fa388cfSLuca Coelho
12481daab1fSLee Jones /*
1259db93491SGil Adam * Generic function for evaluating a method defined in the device specific
1269db93491SGil Adam * method (DSM) interface. The returned acpi object must be freed by calling
1279db93491SGil Adam * function.
1289db93491SGil Adam */
iwl_acpi_get_dsm_object(struct device * dev,int rev,int func,union acpi_object * args,const guid_t * guid)129090a5d7cSLuca Coelho static void *iwl_acpi_get_dsm_object(struct device *dev, int rev, int func,
1304e8fe214SGregory Greenman union acpi_object *args,
1314e8fe214SGregory Greenman const guid_t *guid)
1329db93491SGil Adam {
1339db93491SGil Adam union acpi_object *obj;
1349db93491SGil Adam
1354e8fe214SGregory Greenman obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), guid, rev, func,
1369db93491SGil Adam args);
1379db93491SGil Adam if (!obj) {
1389db93491SGil Adam IWL_DEBUG_DEV_RADIO(dev,
1399db93491SGil Adam "ACPI: DSM method invocation failed (rev: %d, func:%d)\n",
1409db93491SGil Adam rev, func);
1419db93491SGil Adam return ERR_PTR(-ENOENT);
1429db93491SGil Adam }
1439db93491SGil Adam return obj;
1449db93491SGil Adam }
1459db93491SGil Adam
14681daab1fSLee Jones /*
147aefbe5c4SMatt Chen * Generic function to evaluate a DSM with no arguments
148aefbe5c4SMatt Chen * and an integer return value,
149aefbe5c4SMatt Chen * (as an integer object or inside a buffer object),
150aefbe5c4SMatt Chen * verify and assign the value in the "value" parameter.
151aefbe5c4SMatt Chen * return 0 in success and the appropriate errno otherwise.
1529db93491SGil Adam */
iwl_acpi_get_dsm_integer(struct device * dev,int rev,int func,const guid_t * guid,u64 * value,size_t expected_size)153aefbe5c4SMatt Chen static int iwl_acpi_get_dsm_integer(struct device *dev, int rev, int func,
1544e8fe214SGregory Greenman const guid_t *guid, u64 *value,
1554e8fe214SGregory Greenman size_t expected_size)
1569db93491SGil Adam {
1579db93491SGil Adam union acpi_object *obj;
158*d32136e5SMiri Korenblit int ret;
1599db93491SGil Adam
1604e8fe214SGregory Greenman obj = iwl_acpi_get_dsm_object(dev, rev, func, NULL, guid);
161aefbe5c4SMatt Chen if (IS_ERR(obj)) {
162aefbe5c4SMatt Chen IWL_DEBUG_DEV_RADIO(dev,
163aefbe5c4SMatt Chen "Failed to get DSM object. func= %d\n",
164aefbe5c4SMatt Chen func);
1659db93491SGil Adam return -ENOENT;
166aefbe5c4SMatt Chen }
1679db93491SGil Adam
168aefbe5c4SMatt Chen if (obj->type == ACPI_TYPE_INTEGER) {
169aefbe5c4SMatt Chen *value = obj->integer.value;
170aefbe5c4SMatt Chen } else if (obj->type == ACPI_TYPE_BUFFER) {
171aefbe5c4SMatt Chen __le64 le_value = 0;
172aefbe5c4SMatt Chen
173*d32136e5SMiri Korenblit if (WARN_ON_ONCE(expected_size > sizeof(le_value))) {
174*d32136e5SMiri Korenblit ret = -EINVAL;
175*d32136e5SMiri Korenblit goto out;
176*d32136e5SMiri Korenblit }
177aefbe5c4SMatt Chen
178aefbe5c4SMatt Chen /* if the buffer size doesn't match the expected size */
179aefbe5c4SMatt Chen if (obj->buffer.length != expected_size)
180aefbe5c4SMatt Chen IWL_DEBUG_DEV_RADIO(dev,
181aefbe5c4SMatt Chen "ACPI: DSM invalid buffer size, padding or truncating (%d)\n",
182aefbe5c4SMatt Chen obj->buffer.length);
183aefbe5c4SMatt Chen
184aefbe5c4SMatt Chen /* assuming LE from Intel BIOS spec */
185aefbe5c4SMatt Chen memcpy(&le_value, obj->buffer.pointer,
186aefbe5c4SMatt Chen min_t(size_t, expected_size, (size_t)obj->buffer.length));
187aefbe5c4SMatt Chen *value = le64_to_cpu(le_value);
188aefbe5c4SMatt Chen } else {
1899db93491SGil Adam IWL_DEBUG_DEV_RADIO(dev,
1909db93491SGil Adam "ACPI: DSM method did not return a valid object, type=%d\n",
1919db93491SGil Adam obj->type);
1929db93491SGil Adam ret = -EINVAL;
1939db93491SGil Adam goto out;
1949db93491SGil Adam }
1959db93491SGil Adam
1969db93491SGil Adam IWL_DEBUG_DEV_RADIO(dev,
197*d32136e5SMiri Korenblit "ACPI: DSM method evaluated: func=%d, value=%lld\n",
198*d32136e5SMiri Korenblit func, *value);
199*d32136e5SMiri Korenblit ret = 0;
2009db93491SGil Adam out:
2019db93491SGil Adam ACPI_FREE(obj);
2029db93491SGil Adam return ret;
2039db93491SGil Adam }
204aefbe5c4SMatt Chen
205aefbe5c4SMatt Chen /*
206aefbe5c4SMatt Chen * Evaluate a DSM with no arguments and a u8 return value,
207aefbe5c4SMatt Chen */
iwl_acpi_get_dsm_u8(struct device * dev,int rev,int func,const guid_t * guid,u8 * value)2084e8fe214SGregory Greenman int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func,
2094e8fe214SGregory Greenman const guid_t *guid, u8 *value)
210aefbe5c4SMatt Chen {
211aefbe5c4SMatt Chen int ret;
212aefbe5c4SMatt Chen u64 val;
213aefbe5c4SMatt Chen
2144e8fe214SGregory Greenman ret = iwl_acpi_get_dsm_integer(dev, rev, func,
2154e8fe214SGregory Greenman guid, &val, sizeof(u8));
216aefbe5c4SMatt Chen
217aefbe5c4SMatt Chen if (ret < 0)
218aefbe5c4SMatt Chen return ret;
219aefbe5c4SMatt Chen
220aefbe5c4SMatt Chen /* cast val (u64) to be u8 */
221aefbe5c4SMatt Chen *value = (u8)val;
222aefbe5c4SMatt Chen return 0;
223aefbe5c4SMatt Chen }
2249db93491SGil Adam IWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u8);
2259db93491SGil Adam
2267119f02bSMiri Korenblit /*
2277119f02bSMiri Korenblit * Evaluate a DSM with no arguments and a u32 return value,
2287119f02bSMiri Korenblit */
iwl_acpi_get_dsm_u32(struct device * dev,int rev,int func,const guid_t * guid,u32 * value)2297119f02bSMiri Korenblit int iwl_acpi_get_dsm_u32(struct device *dev, int rev, int func,
2307119f02bSMiri Korenblit const guid_t *guid, u32 *value)
2317119f02bSMiri Korenblit {
2327119f02bSMiri Korenblit int ret;
2337119f02bSMiri Korenblit u64 val;
2347119f02bSMiri Korenblit
2357119f02bSMiri Korenblit ret = iwl_acpi_get_dsm_integer(dev, rev, func,
2367119f02bSMiri Korenblit guid, &val, sizeof(u32));
2377119f02bSMiri Korenblit
2387119f02bSMiri Korenblit if (ret < 0)
2397119f02bSMiri Korenblit return ret;
2407119f02bSMiri Korenblit
2417119f02bSMiri Korenblit /* cast val (u64) to be u32 */
2427119f02bSMiri Korenblit *value = (u32)val;
2437119f02bSMiri Korenblit return 0;
2447119f02bSMiri Korenblit }
2457119f02bSMiri Korenblit IWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u32);
2467119f02bSMiri Korenblit
24709396a4fSJohannes Berg static union acpi_object *
iwl_acpi_get_wifi_pkg_range(struct device * dev,union acpi_object * data,int min_data_size,int max_data_size,int * tbl_rev)24809396a4fSJohannes Berg iwl_acpi_get_wifi_pkg_range(struct device *dev,
2492fa388cfSLuca Coelho union acpi_object *data,
25097f8a3d1SAyala Barazani int min_data_size,
25197f8a3d1SAyala Barazani int max_data_size,
25297f8a3d1SAyala Barazani int *tbl_rev)
2532fa388cfSLuca Coelho {
2542fa388cfSLuca Coelho int i;
2552fa388cfSLuca Coelho union acpi_object *wifi_pkg;
2562fa388cfSLuca Coelho
2572fa388cfSLuca Coelho /*
2582fa388cfSLuca Coelho * We need at least one entry in the wifi package that
2592fa388cfSLuca Coelho * describes the domain, and one more entry, otherwise there's
2602fa388cfSLuca Coelho * no point in reading it.
2612fa388cfSLuca Coelho */
26297f8a3d1SAyala Barazani if (WARN_ON_ONCE(min_data_size < 2 || min_data_size > max_data_size))
2632fa388cfSLuca Coelho return ERR_PTR(-EINVAL);
2642fa388cfSLuca Coelho
2652fa388cfSLuca Coelho /*
2662fa388cfSLuca Coelho * We need at least two packages, one for the revision and one
2672fa388cfSLuca Coelho * for the data itself. Also check that the revision is valid
268e12cfc7bSMiri Korenblit * (i.e. it is an integer (each caller has to check by itself
269e12cfc7bSMiri Korenblit * if the returned revision is supported)).
2702fa388cfSLuca Coelho */
2712fa388cfSLuca Coelho if (data->type != ACPI_TYPE_PACKAGE ||
2722fa388cfSLuca Coelho data->package.count < 2 ||
273e12cfc7bSMiri Korenblit data->package.elements[0].type != ACPI_TYPE_INTEGER) {
274e12cfc7bSMiri Korenblit IWL_DEBUG_DEV_RADIO(dev, "Invalid packages structure\n");
2752fa388cfSLuca Coelho return ERR_PTR(-EINVAL);
2762fa388cfSLuca Coelho }
2772fa388cfSLuca Coelho
2780c3d7282SHaim Dreyfuss *tbl_rev = data->package.elements[0].integer.value;
2790c3d7282SHaim Dreyfuss
2802fa388cfSLuca Coelho /* loop through all the packages to find the one for WiFi */
2812fa388cfSLuca Coelho for (i = 1; i < data->package.count; i++) {
2822fa388cfSLuca Coelho union acpi_object *domain;
2832fa388cfSLuca Coelho
2842fa388cfSLuca Coelho wifi_pkg = &data->package.elements[i];
2852fa388cfSLuca Coelho
2862fa388cfSLuca Coelho /* skip entries that are not a package with the right size */
2872fa388cfSLuca Coelho if (wifi_pkg->type != ACPI_TYPE_PACKAGE ||
28897f8a3d1SAyala Barazani wifi_pkg->package.count < min_data_size ||
28997f8a3d1SAyala Barazani wifi_pkg->package.count > max_data_size)
2902fa388cfSLuca Coelho continue;
2912fa388cfSLuca Coelho
2922fa388cfSLuca Coelho domain = &wifi_pkg->package.elements[0];
2932fa388cfSLuca Coelho if (domain->type == ACPI_TYPE_INTEGER &&
2942fa388cfSLuca Coelho domain->integer.value == ACPI_WIFI_DOMAIN)
2952fa388cfSLuca Coelho goto found;
2962fa388cfSLuca Coelho }
2972fa388cfSLuca Coelho
2982fa388cfSLuca Coelho return ERR_PTR(-ENOENT);
2992fa388cfSLuca Coelho
3002fa388cfSLuca Coelho found:
3012fa388cfSLuca Coelho return wifi_pkg;
3022fa388cfSLuca Coelho }
30309396a4fSJohannes Berg
30409396a4fSJohannes Berg static union acpi_object *
iwl_acpi_get_wifi_pkg(struct device * dev,union acpi_object * data,int data_size,int * tbl_rev)30509396a4fSJohannes Berg iwl_acpi_get_wifi_pkg(struct device *dev,
30609396a4fSJohannes Berg union acpi_object *data,
30709396a4fSJohannes Berg int data_size, int *tbl_rev)
30809396a4fSJohannes Berg {
30909396a4fSJohannes Berg return iwl_acpi_get_wifi_pkg_range(dev, data, data_size, data_size,
31009396a4fSJohannes Berg tbl_rev);
31109396a4fSJohannes Berg }
31209396a4fSJohannes Berg
31345f65569SLuca Coelho
iwl_acpi_get_tas(struct iwl_fw_runtime * fwrt,union iwl_tas_config_cmd * cmd,int fw_ver)31428dd7ccdSMordechay Goodstein int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt,
3156da7ba3aSAyala Barazani union iwl_tas_config_cmd *cmd, int fw_ver)
31628dd7ccdSMordechay Goodstein {
31728dd7ccdSMordechay Goodstein union acpi_object *wifi_pkg, *data;
3187c530588SMiri Korenblit int ret, tbl_rev, i, block_list_size, enabled;
31928dd7ccdSMordechay Goodstein
32028dd7ccdSMordechay Goodstein data = iwl_acpi_get_object(fwrt->dev, ACPI_WTAS_METHOD);
32128dd7ccdSMordechay Goodstein if (IS_ERR(data))
32228dd7ccdSMordechay Goodstein return PTR_ERR(data);
32328dd7ccdSMordechay Goodstein
3247c530588SMiri Korenblit /* try to read wtas table revision 1 or revision 0*/
32528dd7ccdSMordechay Goodstein wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
32628dd7ccdSMordechay Goodstein ACPI_WTAS_WIFI_DATA_SIZE,
32728dd7ccdSMordechay Goodstein &tbl_rev);
32828dd7ccdSMordechay Goodstein if (IS_ERR(wifi_pkg)) {
32928dd7ccdSMordechay Goodstein ret = PTR_ERR(wifi_pkg);
33028dd7ccdSMordechay Goodstein goto out_free;
33128dd7ccdSMordechay Goodstein }
33228dd7ccdSMordechay Goodstein
3337c530588SMiri Korenblit if (tbl_rev == 1 && wifi_pkg->package.elements[1].type ==
3347c530588SMiri Korenblit ACPI_TYPE_INTEGER) {
3357c530588SMiri Korenblit u32 tas_selection =
3367c530588SMiri Korenblit (u32)wifi_pkg->package.elements[1].integer.value;
3377c530588SMiri Korenblit u16 override_iec =
3387c530588SMiri Korenblit (tas_selection & ACPI_WTAS_OVERRIDE_IEC_MSK) >> ACPI_WTAS_OVERRIDE_IEC_POS;
3397c530588SMiri Korenblit u16 enabled_iec = (tas_selection & ACPI_WTAS_ENABLE_IEC_MSK) >>
3407c530588SMiri Korenblit ACPI_WTAS_ENABLE_IEC_POS;
3416da7ba3aSAyala Barazani u8 usa_tas_uhb = (tas_selection & ACPI_WTAS_USA_UHB_MSK) >> ACPI_WTAS_USA_UHB_POS;
3426da7ba3aSAyala Barazani
3437c530588SMiri Korenblit
3447c530588SMiri Korenblit enabled = tas_selection & ACPI_WTAS_ENABLED_MSK;
3456da7ba3aSAyala Barazani if (fw_ver <= 3) {
3466da7ba3aSAyala Barazani cmd->v3.override_tas_iec = cpu_to_le16(override_iec);
3476da7ba3aSAyala Barazani cmd->v3.enable_tas_iec = cpu_to_le16(enabled_iec);
3486da7ba3aSAyala Barazani } else {
3496da7ba3aSAyala Barazani cmd->v4.usa_tas_uhb_allowed = usa_tas_uhb;
3506da7ba3aSAyala Barazani cmd->v4.override_tas_iec = (u8)override_iec;
3516da7ba3aSAyala Barazani cmd->v4.enable_tas_iec = (u8)enabled_iec;
3526da7ba3aSAyala Barazani }
3537c530588SMiri Korenblit
3547c530588SMiri Korenblit } else if (tbl_rev == 0 &&
3557c530588SMiri Korenblit wifi_pkg->package.elements[1].type == ACPI_TYPE_INTEGER) {
3567c530588SMiri Korenblit enabled = !!wifi_pkg->package.elements[1].integer.value;
3577c530588SMiri Korenblit } else {
35828dd7ccdSMordechay Goodstein ret = -EINVAL;
35928dd7ccdSMordechay Goodstein goto out_free;
36028dd7ccdSMordechay Goodstein }
36128dd7ccdSMordechay Goodstein
36228dd7ccdSMordechay Goodstein if (!enabled) {
36328dd7ccdSMordechay Goodstein IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n");
36428dd7ccdSMordechay Goodstein ret = 0;
36528dd7ccdSMordechay Goodstein goto out_free;
36628dd7ccdSMordechay Goodstein }
36728dd7ccdSMordechay Goodstein
3687c530588SMiri Korenblit IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", tbl_rev);
36919426d54SAbhishek Naik if (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER ||
37019426d54SAbhishek Naik wifi_pkg->package.elements[2].integer.value >
37128dd7ccdSMordechay Goodstein APCI_WTAS_BLACK_LIST_MAX) {
37228dd7ccdSMordechay Goodstein IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %llu\n",
3737c530588SMiri Korenblit wifi_pkg->package.elements[2].integer.value);
37428dd7ccdSMordechay Goodstein ret = -EINVAL;
37528dd7ccdSMordechay Goodstein goto out_free;
37628dd7ccdSMordechay Goodstein }
3777c530588SMiri Korenblit block_list_size = wifi_pkg->package.elements[2].integer.value;
3786da7ba3aSAyala Barazani cmd->v4.block_list_size = cpu_to_le32(block_list_size);
37928dd7ccdSMordechay Goodstein
3807c530588SMiri Korenblit IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", block_list_size);
3817c530588SMiri Korenblit if (block_list_size > APCI_WTAS_BLACK_LIST_MAX) {
38228dd7ccdSMordechay Goodstein IWL_DEBUG_RADIO(fwrt, "TAS invalid array size value %u\n",
3837c530588SMiri Korenblit block_list_size);
38428dd7ccdSMordechay Goodstein ret = -EINVAL;
38528dd7ccdSMordechay Goodstein goto out_free;
38628dd7ccdSMordechay Goodstein }
38728dd7ccdSMordechay Goodstein
3887c530588SMiri Korenblit for (i = 0; i < block_list_size; i++) {
38928dd7ccdSMordechay Goodstein u32 country;
39028dd7ccdSMordechay Goodstein
39119426d54SAbhishek Naik if (wifi_pkg->package.elements[3 + i].type !=
39228dd7ccdSMordechay Goodstein ACPI_TYPE_INTEGER) {
39328dd7ccdSMordechay Goodstein IWL_DEBUG_RADIO(fwrt,
39419426d54SAbhishek Naik "TAS invalid array elem %d\n", 3 + i);
39528dd7ccdSMordechay Goodstein ret = -EINVAL;
39628dd7ccdSMordechay Goodstein goto out_free;
39728dd7ccdSMordechay Goodstein }
39828dd7ccdSMordechay Goodstein
39919426d54SAbhishek Naik country = wifi_pkg->package.elements[3 + i].integer.value;
4006da7ba3aSAyala Barazani cmd->v4.block_list_array[i] = cpu_to_le32(country);
401cdaba917SEmmanuel Grumbach IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country);
40228dd7ccdSMordechay Goodstein }
40328dd7ccdSMordechay Goodstein
4047c530588SMiri Korenblit ret = 1;
40528dd7ccdSMordechay Goodstein out_free:
40628dd7ccdSMordechay Goodstein kfree(data);
40728dd7ccdSMordechay Goodstein return ret;
40828dd7ccdSMordechay Goodstein }
40928dd7ccdSMordechay Goodstein IWL_EXPORT_SYMBOL(iwl_acpi_get_tas);
41028dd7ccdSMordechay Goodstein
iwl_acpi_get_mcc(struct device * dev,char * mcc)41145f65569SLuca Coelho int iwl_acpi_get_mcc(struct device *dev, char *mcc)
41245f65569SLuca Coelho {
41345f65569SLuca Coelho union acpi_object *wifi_pkg, *data;
41445f65569SLuca Coelho u32 mcc_val;
4150c3d7282SHaim Dreyfuss int ret, tbl_rev;
41645f65569SLuca Coelho
41745f65569SLuca Coelho data = iwl_acpi_get_object(dev, ACPI_WRDD_METHOD);
41845f65569SLuca Coelho if (IS_ERR(data))
41945f65569SLuca Coelho return PTR_ERR(data);
42045f65569SLuca Coelho
4210c3d7282SHaim Dreyfuss wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE,
4220c3d7282SHaim Dreyfuss &tbl_rev);
4233ed83da3SLuca Coelho if (IS_ERR(wifi_pkg)) {
42445f65569SLuca Coelho ret = PTR_ERR(wifi_pkg);
42545f65569SLuca Coelho goto out_free;
42645f65569SLuca Coelho }
42745f65569SLuca Coelho
4283ed83da3SLuca Coelho if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
4293ed83da3SLuca Coelho tbl_rev != 0) {
43045f65569SLuca Coelho ret = -EINVAL;
43145f65569SLuca Coelho goto out_free;
43245f65569SLuca Coelho }
43345f65569SLuca Coelho
43445f65569SLuca Coelho mcc_val = wifi_pkg->package.elements[1].integer.value;
43545f65569SLuca Coelho
43645f65569SLuca Coelho mcc[0] = (mcc_val >> 8) & 0xff;
43745f65569SLuca Coelho mcc[1] = mcc_val & 0xff;
43845f65569SLuca Coelho mcc[2] = '\0';
43945f65569SLuca Coelho
44045f65569SLuca Coelho ret = 0;
44145f65569SLuca Coelho out_free:
44245f65569SLuca Coelho kfree(data);
44345f65569SLuca Coelho return ret;
44445f65569SLuca Coelho }
44545f65569SLuca Coelho IWL_EXPORT_SYMBOL(iwl_acpi_get_mcc);
4461184611eSLuca Coelho
iwl_acpi_get_pwr_limit(struct device * dev)4471184611eSLuca Coelho u64 iwl_acpi_get_pwr_limit(struct device *dev)
4481184611eSLuca Coelho {
4491184611eSLuca Coelho union acpi_object *data, *wifi_pkg;
4501184611eSLuca Coelho u64 dflt_pwr_limit;
4510c3d7282SHaim Dreyfuss int tbl_rev;
4521184611eSLuca Coelho
4531184611eSLuca Coelho data = iwl_acpi_get_object(dev, ACPI_SPLC_METHOD);
4541184611eSLuca Coelho if (IS_ERR(data)) {
4551184611eSLuca Coelho dflt_pwr_limit = 0;
4561184611eSLuca Coelho goto out;
4571184611eSLuca Coelho }
4581184611eSLuca Coelho
4591184611eSLuca Coelho wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data,
4600c3d7282SHaim Dreyfuss ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev);
4610c3d7282SHaim Dreyfuss if (IS_ERR(wifi_pkg) || tbl_rev != 0 ||
4621184611eSLuca Coelho wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) {
4631184611eSLuca Coelho dflt_pwr_limit = 0;
4641184611eSLuca Coelho goto out_free;
4651184611eSLuca Coelho }
4661184611eSLuca Coelho
4671184611eSLuca Coelho dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value;
4681184611eSLuca Coelho out_free:
4691184611eSLuca Coelho kfree(data);
4701184611eSLuca Coelho out:
4711184611eSLuca Coelho return dflt_pwr_limit;
4721184611eSLuca Coelho }
4731184611eSLuca Coelho IWL_EXPORT_SYMBOL(iwl_acpi_get_pwr_limit);
47448e775e6SHaim Dreyfuss
iwl_acpi_get_eckv(struct device * dev,u32 * extl_clk)47548e775e6SHaim Dreyfuss int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk)
47648e775e6SHaim Dreyfuss {
47748e775e6SHaim Dreyfuss union acpi_object *wifi_pkg, *data;
4780c3d7282SHaim Dreyfuss int ret, tbl_rev;
47948e775e6SHaim Dreyfuss
48048e775e6SHaim Dreyfuss data = iwl_acpi_get_object(dev, ACPI_ECKV_METHOD);
48148e775e6SHaim Dreyfuss if (IS_ERR(data))
48248e775e6SHaim Dreyfuss return PTR_ERR(data);
48348e775e6SHaim Dreyfuss
4840c3d7282SHaim Dreyfuss wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_ECKV_WIFI_DATA_SIZE,
4850c3d7282SHaim Dreyfuss &tbl_rev);
4863ed83da3SLuca Coelho if (IS_ERR(wifi_pkg)) {
48748e775e6SHaim Dreyfuss ret = PTR_ERR(wifi_pkg);
48848e775e6SHaim Dreyfuss goto out_free;
48948e775e6SHaim Dreyfuss }
49048e775e6SHaim Dreyfuss
4913ed83da3SLuca Coelho if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
4923ed83da3SLuca Coelho tbl_rev != 0) {
49348e775e6SHaim Dreyfuss ret = -EINVAL;
49448e775e6SHaim Dreyfuss goto out_free;
49548e775e6SHaim Dreyfuss }
49648e775e6SHaim Dreyfuss
49748e775e6SHaim Dreyfuss *extl_clk = wifi_pkg->package.elements[1].integer.value;
49848e775e6SHaim Dreyfuss
49948e775e6SHaim Dreyfuss ret = 0;
50048e775e6SHaim Dreyfuss
50148e775e6SHaim Dreyfuss out_free:
50248e775e6SHaim Dreyfuss kfree(data);
50348e775e6SHaim Dreyfuss return ret;
50448e775e6SHaim Dreyfuss }
50548e775e6SHaim Dreyfuss IWL_EXPORT_SYMBOL(iwl_acpi_get_eckv);
50639c1a972SIhab Zhaika
iwl_sar_set_profile(union acpi_object * table,struct iwl_sar_profile * profile,bool enabled,u8 num_chains,u8 num_sub_bands)507090a5d7cSLuca Coelho static int iwl_sar_set_profile(union acpi_object *table,
50839c1a972SIhab Zhaika struct iwl_sar_profile *profile,
5098ecf0477SLuca Coelho bool enabled, u8 num_chains, u8 num_sub_bands)
51039c1a972SIhab Zhaika {
51181870d13SLuca Coelho int i, j, idx = 0;
51239c1a972SIhab Zhaika
51381870d13SLuca Coelho /*
51481870d13SLuca Coelho * The table from ACPI is flat, but we store it in a
51581870d13SLuca Coelho * structured array.
51681870d13SLuca Coelho */
51740063f60SLuca Coelho for (i = 0; i < ACPI_SAR_NUM_CHAINS_REV2; i++) {
51840063f60SLuca Coelho for (j = 0; j < ACPI_SAR_NUM_SUB_BANDS_REV2; j++) {
51940063f60SLuca Coelho /* if we don't have the values, use the default */
52040063f60SLuca Coelho if (i >= num_chains || j >= num_sub_bands) {
52140063f60SLuca Coelho profile->chains[i].subbands[j] = 0;
52240063f60SLuca Coelho } else {
52381870d13SLuca Coelho if (table[idx].type != ACPI_TYPE_INTEGER ||
52481870d13SLuca Coelho table[idx].integer.value > U8_MAX)
52539c1a972SIhab Zhaika return -EINVAL;
52639c1a972SIhab Zhaika
52781870d13SLuca Coelho profile->chains[i].subbands[j] =
52881870d13SLuca Coelho table[idx].integer.value;
52981870d13SLuca Coelho
53081870d13SLuca Coelho idx++;
53181870d13SLuca Coelho }
53239c1a972SIhab Zhaika }
53340063f60SLuca Coelho }
53439c1a972SIhab Zhaika
53578a19d52SMiri Korenblit /* Only if all values were valid can the profile be enabled */
53678a19d52SMiri Korenblit profile->enabled = enabled;
53778a19d52SMiri Korenblit
53839c1a972SIhab Zhaika return 0;
53939c1a972SIhab Zhaika }
54039c1a972SIhab Zhaika
iwl_sar_fill_table(struct iwl_fw_runtime * fwrt,__le16 * per_chain,u32 n_subbands,int prof_a,int prof_b)5419c08cef8SLuca Coelho static int iwl_sar_fill_table(struct iwl_fw_runtime *fwrt,
5429c08cef8SLuca Coelho __le16 *per_chain, u32 n_subbands,
54339c1a972SIhab Zhaika int prof_a, int prof_b)
54439c1a972SIhab Zhaika {
5452a808414SLuca Coelho int profs[ACPI_SAR_NUM_CHAINS_REV0] = { prof_a, prof_b };
54681870d13SLuca Coelho int i, j;
54739c1a972SIhab Zhaika
5482a808414SLuca Coelho for (i = 0; i < ACPI_SAR_NUM_CHAINS_REV0; i++) {
54939c1a972SIhab Zhaika struct iwl_sar_profile *prof;
55039c1a972SIhab Zhaika
55139c1a972SIhab Zhaika /* don't allow SAR to be disabled (profile 0 means disable) */
55239c1a972SIhab Zhaika if (profs[i] == 0)
55339c1a972SIhab Zhaika return -EPERM;
55439c1a972SIhab Zhaika
55539c1a972SIhab Zhaika /* we are off by one, so allow up to ACPI_SAR_PROFILE_NUM */
55639c1a972SIhab Zhaika if (profs[i] > ACPI_SAR_PROFILE_NUM)
55739c1a972SIhab Zhaika return -EINVAL;
55839c1a972SIhab Zhaika
55939c1a972SIhab Zhaika /* profiles go from 1 to 4, so decrement to access the array */
56039c1a972SIhab Zhaika prof = &fwrt->sar_profiles[profs[i] - 1];
56139c1a972SIhab Zhaika
56239c1a972SIhab Zhaika /* if the profile is disabled, do nothing */
56339c1a972SIhab Zhaika if (!prof->enabled) {
56439c1a972SIhab Zhaika IWL_DEBUG_RADIO(fwrt, "SAR profile %d is disabled.\n",
56539c1a972SIhab Zhaika profs[i]);
5661edd56e6SLuca Coelho /*
5671edd56e6SLuca Coelho * if one of the profiles is disabled, we
5681edd56e6SLuca Coelho * ignore all of them and return 1 to
5691edd56e6SLuca Coelho * differentiate disabled from other failures.
5701edd56e6SLuca Coelho */
5711edd56e6SLuca Coelho return 1;
57239c1a972SIhab Zhaika }
5731edd56e6SLuca Coelho
57439c1a972SIhab Zhaika IWL_DEBUG_INFO(fwrt,
57539c1a972SIhab Zhaika "SAR EWRD: chain %d profile index %d\n",
57639c1a972SIhab Zhaika i, profs[i]);
57739c1a972SIhab Zhaika IWL_DEBUG_RADIO(fwrt, " Chain[%d]:\n", i);
5789c08cef8SLuca Coelho for (j = 0; j < n_subbands; j++) {
5799c08cef8SLuca Coelho per_chain[i * n_subbands + j] =
58081870d13SLuca Coelho cpu_to_le16(prof->chains[i].subbands[j]);
58139c1a972SIhab Zhaika IWL_DEBUG_RADIO(fwrt, " Band[%d] = %d * .125dBm\n",
58281870d13SLuca Coelho j, prof->chains[i].subbands[j]);
58339c1a972SIhab Zhaika }
58439c1a972SIhab Zhaika }
58539c1a972SIhab Zhaika
58639c1a972SIhab Zhaika return 0;
58739c1a972SIhab Zhaika }
5889c08cef8SLuca Coelho
iwl_sar_select_profile(struct iwl_fw_runtime * fwrt,__le16 * per_chain,u32 n_tables,u32 n_subbands,int prof_a,int prof_b)5899c08cef8SLuca Coelho int iwl_sar_select_profile(struct iwl_fw_runtime *fwrt,
5909c08cef8SLuca Coelho __le16 *per_chain, u32 n_tables, u32 n_subbands,
5919c08cef8SLuca Coelho int prof_a, int prof_b)
5929c08cef8SLuca Coelho {
5939c08cef8SLuca Coelho int i, ret = 0;
5949c08cef8SLuca Coelho
5959c08cef8SLuca Coelho for (i = 0; i < n_tables; i++) {
5969c08cef8SLuca Coelho ret = iwl_sar_fill_table(fwrt,
5972a808414SLuca Coelho &per_chain[i * n_subbands * ACPI_SAR_NUM_CHAINS_REV0],
5989c08cef8SLuca Coelho n_subbands, prof_a, prof_b);
5999c08cef8SLuca Coelho if (ret)
6009c08cef8SLuca Coelho break;
6019c08cef8SLuca Coelho }
6029c08cef8SLuca Coelho
6039c08cef8SLuca Coelho return ret;
6049c08cef8SLuca Coelho }
60539c1a972SIhab Zhaika IWL_EXPORT_SYMBOL(iwl_sar_select_profile);
60639c1a972SIhab Zhaika
iwl_sar_get_wrds_table(struct iwl_fw_runtime * fwrt)60739c1a972SIhab Zhaika int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt)
60839c1a972SIhab Zhaika {
60939c1a972SIhab Zhaika union acpi_object *wifi_pkg, *table, *data;
61039c1a972SIhab Zhaika int ret, tbl_rev;
611b0aa02b3SAyala Barazani u32 flags;
6122a808414SLuca Coelho u8 num_chains, num_sub_bands;
61339c1a972SIhab Zhaika
61439c1a972SIhab Zhaika data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDS_METHOD);
61539c1a972SIhab Zhaika if (IS_ERR(data))
61639c1a972SIhab Zhaika return PTR_ERR(data);
61739c1a972SIhab Zhaika
6182a808414SLuca Coelho /* start by trying to read revision 2 */
61939c1a972SIhab Zhaika wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
6202a808414SLuca Coelho ACPI_WRDS_WIFI_DATA_SIZE_REV2,
6212a808414SLuca Coelho &tbl_rev);
6222a808414SLuca Coelho if (!IS_ERR(wifi_pkg)) {
6232a808414SLuca Coelho if (tbl_rev != 2) {
62483f4bf71SDan Carpenter ret = -EINVAL;
62539c1a972SIhab Zhaika goto out_free;
62639c1a972SIhab Zhaika }
62739c1a972SIhab Zhaika
6282a808414SLuca Coelho num_chains = ACPI_SAR_NUM_CHAINS_REV2;
6292a808414SLuca Coelho num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;
6302a808414SLuca Coelho
6312a808414SLuca Coelho goto read_table;
6322a808414SLuca Coelho }
6332a808414SLuca Coelho
6342a808414SLuca Coelho /* then try revision 1 */
6352a808414SLuca Coelho wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
6362a808414SLuca Coelho ACPI_WRDS_WIFI_DATA_SIZE_REV1,
6372a808414SLuca Coelho &tbl_rev);
6382a808414SLuca Coelho if (!IS_ERR(wifi_pkg)) {
6392a808414SLuca Coelho if (tbl_rev != 1) {
64083f4bf71SDan Carpenter ret = -EINVAL;
64155ae96b6SHaim Dreyfuss goto out_free;
64255ae96b6SHaim Dreyfuss }
64355ae96b6SHaim Dreyfuss
6442a808414SLuca Coelho num_chains = ACPI_SAR_NUM_CHAINS_REV1;
6452a808414SLuca Coelho num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;
6462a808414SLuca Coelho
6472a808414SLuca Coelho goto read_table;
6482a808414SLuca Coelho }
6492a808414SLuca Coelho
6502a808414SLuca Coelho /* then finally revision 0 */
6512a808414SLuca Coelho wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
6522a808414SLuca Coelho ACPI_WRDS_WIFI_DATA_SIZE_REV0,
6532a808414SLuca Coelho &tbl_rev);
6542a808414SLuca Coelho if (!IS_ERR(wifi_pkg)) {
6552a808414SLuca Coelho if (tbl_rev != 0) {
65683f4bf71SDan Carpenter ret = -EINVAL;
6572a808414SLuca Coelho goto out_free;
6582a808414SLuca Coelho }
6592a808414SLuca Coelho
6602a808414SLuca Coelho num_chains = ACPI_SAR_NUM_CHAINS_REV0;
6612a808414SLuca Coelho num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;
6622a808414SLuca Coelho
6632a808414SLuca Coelho goto read_table;
6642a808414SLuca Coelho }
6652a808414SLuca Coelho
6662a808414SLuca Coelho ret = PTR_ERR(wifi_pkg);
6672a808414SLuca Coelho goto out_free;
6682a808414SLuca Coelho
6692a808414SLuca Coelho read_table:
67039c1a972SIhab Zhaika if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
67139c1a972SIhab Zhaika ret = -EINVAL;
67239c1a972SIhab Zhaika goto out_free;
67339c1a972SIhab Zhaika }
67439c1a972SIhab Zhaika
6752a808414SLuca Coelho IWL_DEBUG_RADIO(fwrt, "Reading WRDS tbl_rev=%d\n", tbl_rev);
6762a808414SLuca Coelho
677b0aa02b3SAyala Barazani flags = wifi_pkg->package.elements[1].integer.value;
678b0aa02b3SAyala Barazani fwrt->reduced_power_flags = flags >> IWL_REDUCE_POWER_FLAGS_POS;
67939c1a972SIhab Zhaika
68039c1a972SIhab Zhaika /* position of the actual table */
68139c1a972SIhab Zhaika table = &wifi_pkg->package.elements[2];
68239c1a972SIhab Zhaika
68339c1a972SIhab Zhaika /* The profile from WRDS is officially profile 1, but goes
68439c1a972SIhab Zhaika * into sar_profiles[0] (because we don't have a profile 0).
68539c1a972SIhab Zhaika */
686b0aa02b3SAyala Barazani ret = iwl_sar_set_profile(table, &fwrt->sar_profiles[0],
687b0aa02b3SAyala Barazani flags & IWL_SAR_ENABLE_MSK,
6882a808414SLuca Coelho num_chains, num_sub_bands);
68939c1a972SIhab Zhaika out_free:
69039c1a972SIhab Zhaika kfree(data);
69139c1a972SIhab Zhaika return ret;
69239c1a972SIhab Zhaika }
69339c1a972SIhab Zhaika IWL_EXPORT_SYMBOL(iwl_sar_get_wrds_table);
69439c1a972SIhab Zhaika
iwl_sar_get_ewrd_table(struct iwl_fw_runtime * fwrt)69539c1a972SIhab Zhaika int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt)
69639c1a972SIhab Zhaika {
69739c1a972SIhab Zhaika union acpi_object *wifi_pkg, *data;
69839c1a972SIhab Zhaika bool enabled;
699fb3c06cfSLuca Coelho int i, n_profiles, tbl_rev, pos;
70039c1a972SIhab Zhaika int ret = 0;
70151266c11SLuca Coelho u8 num_chains, num_sub_bands;
70239c1a972SIhab Zhaika
70339c1a972SIhab Zhaika data = iwl_acpi_get_object(fwrt->dev, ACPI_EWRD_METHOD);
70439c1a972SIhab Zhaika if (IS_ERR(data))
70539c1a972SIhab Zhaika return PTR_ERR(data);
70639c1a972SIhab Zhaika
70751266c11SLuca Coelho /* start by trying to read revision 2 */
70839c1a972SIhab Zhaika wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
70951266c11SLuca Coelho ACPI_EWRD_WIFI_DATA_SIZE_REV2,
71051266c11SLuca Coelho &tbl_rev);
71151266c11SLuca Coelho if (!IS_ERR(wifi_pkg)) {
71251266c11SLuca Coelho if (tbl_rev != 2) {
71383f4bf71SDan Carpenter ret = -EINVAL;
71439c1a972SIhab Zhaika goto out_free;
71539c1a972SIhab Zhaika }
71639c1a972SIhab Zhaika
71751266c11SLuca Coelho num_chains = ACPI_SAR_NUM_CHAINS_REV2;
71851266c11SLuca Coelho num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;
71951266c11SLuca Coelho
72051266c11SLuca Coelho goto read_table;
72151266c11SLuca Coelho }
72251266c11SLuca Coelho
72351266c11SLuca Coelho /* then try revision 1 */
72451266c11SLuca Coelho wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
72551266c11SLuca Coelho ACPI_EWRD_WIFI_DATA_SIZE_REV1,
72651266c11SLuca Coelho &tbl_rev);
72751266c11SLuca Coelho if (!IS_ERR(wifi_pkg)) {
72851266c11SLuca Coelho if (tbl_rev != 1) {
72983f4bf71SDan Carpenter ret = -EINVAL;
73055ae96b6SHaim Dreyfuss goto out_free;
73155ae96b6SHaim Dreyfuss }
73255ae96b6SHaim Dreyfuss
73351266c11SLuca Coelho num_chains = ACPI_SAR_NUM_CHAINS_REV1;
73451266c11SLuca Coelho num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;
73551266c11SLuca Coelho
73651266c11SLuca Coelho goto read_table;
73751266c11SLuca Coelho }
73851266c11SLuca Coelho
73951266c11SLuca Coelho /* then finally revision 0 */
74051266c11SLuca Coelho wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
74151266c11SLuca Coelho ACPI_EWRD_WIFI_DATA_SIZE_REV0,
74251266c11SLuca Coelho &tbl_rev);
74351266c11SLuca Coelho if (!IS_ERR(wifi_pkg)) {
74451266c11SLuca Coelho if (tbl_rev != 0) {
74583f4bf71SDan Carpenter ret = -EINVAL;
74651266c11SLuca Coelho goto out_free;
74751266c11SLuca Coelho }
74851266c11SLuca Coelho
74951266c11SLuca Coelho num_chains = ACPI_SAR_NUM_CHAINS_REV0;
75051266c11SLuca Coelho num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;
75151266c11SLuca Coelho
75251266c11SLuca Coelho goto read_table;
75351266c11SLuca Coelho }
75451266c11SLuca Coelho
75551266c11SLuca Coelho ret = PTR_ERR(wifi_pkg);
75651266c11SLuca Coelho goto out_free;
75751266c11SLuca Coelho
75851266c11SLuca Coelho read_table:
75939c1a972SIhab Zhaika if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
76039c1a972SIhab Zhaika wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER) {
76139c1a972SIhab Zhaika ret = -EINVAL;
76239c1a972SIhab Zhaika goto out_free;
76339c1a972SIhab Zhaika }
76439c1a972SIhab Zhaika
76539c1a972SIhab Zhaika enabled = !!(wifi_pkg->package.elements[1].integer.value);
76639c1a972SIhab Zhaika n_profiles = wifi_pkg->package.elements[2].integer.value;
76739c1a972SIhab Zhaika
76839c1a972SIhab Zhaika /*
76939c1a972SIhab Zhaika * Check the validity of n_profiles. The EWRD profiles start
77039c1a972SIhab Zhaika * from index 1, so the maximum value allowed here is
77139c1a972SIhab Zhaika * ACPI_SAR_PROFILES_NUM - 1.
77239c1a972SIhab Zhaika */
77350343117SMiri Korenblit if (n_profiles >= ACPI_SAR_PROFILE_NUM) {
77439c1a972SIhab Zhaika ret = -EINVAL;
77539c1a972SIhab Zhaika goto out_free;
77639c1a972SIhab Zhaika }
77739c1a972SIhab Zhaika
77839c1a972SIhab Zhaika /* the tables start at element 3 */
779fb3c06cfSLuca Coelho pos = 3;
78039c1a972SIhab Zhaika
781fb3c06cfSLuca Coelho for (i = 0; i < n_profiles; i++) {
78239c1a972SIhab Zhaika /* The EWRD profiles officially go from 2 to 4, but we
78339c1a972SIhab Zhaika * save them in sar_profiles[1-3] (because we don't
78439c1a972SIhab Zhaika * have profile 0). So in the array we start from 1.
78539c1a972SIhab Zhaika */
78639c1a972SIhab Zhaika ret = iwl_sar_set_profile(&wifi_pkg->package.elements[pos],
7872a808414SLuca Coelho &fwrt->sar_profiles[i + 1], enabled,
78851266c11SLuca Coelho num_chains, num_sub_bands);
78939c1a972SIhab Zhaika if (ret < 0)
79039c1a972SIhab Zhaika break;
79139c1a972SIhab Zhaika
79239c1a972SIhab Zhaika /* go to the next table */
79351266c11SLuca Coelho pos += num_chains * num_sub_bands;
79439c1a972SIhab Zhaika }
79539c1a972SIhab Zhaika
79639c1a972SIhab Zhaika out_free:
79739c1a972SIhab Zhaika kfree(data);
79839c1a972SIhab Zhaika return ret;
79939c1a972SIhab Zhaika }
80039c1a972SIhab Zhaika IWL_EXPORT_SYMBOL(iwl_sar_get_ewrd_table);
80139c1a972SIhab Zhaika
iwl_sar_get_wgds_table(struct iwl_fw_runtime * fwrt)80239c1a972SIhab Zhaika int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt)
80339c1a972SIhab Zhaika {
80439c1a972SIhab Zhaika union acpi_object *wifi_pkg, *data;
8055bf7a9edSLuca Coelho int i, j, k, ret, tbl_rev;
80697f8a3d1SAyala Barazani u8 num_bands, num_profiles;
80797f8a3d1SAyala Barazani static const struct {
80897f8a3d1SAyala Barazani u8 revisions;
80997f8a3d1SAyala Barazani u8 bands;
81097f8a3d1SAyala Barazani u8 profiles;
81197f8a3d1SAyala Barazani u8 min_profiles;
81297f8a3d1SAyala Barazani } rev_data[] = {
81397f8a3d1SAyala Barazani {
81497f8a3d1SAyala Barazani .revisions = BIT(3),
81597f8a3d1SAyala Barazani .bands = ACPI_GEO_NUM_BANDS_REV2,
81697f8a3d1SAyala Barazani .profiles = ACPI_NUM_GEO_PROFILES_REV3,
81797f8a3d1SAyala Barazani .min_profiles = 3,
81897f8a3d1SAyala Barazani },
81997f8a3d1SAyala Barazani {
82097f8a3d1SAyala Barazani .revisions = BIT(2),
82197f8a3d1SAyala Barazani .bands = ACPI_GEO_NUM_BANDS_REV2,
82297f8a3d1SAyala Barazani .profiles = ACPI_NUM_GEO_PROFILES,
82397f8a3d1SAyala Barazani },
82497f8a3d1SAyala Barazani {
82597f8a3d1SAyala Barazani .revisions = BIT(0) | BIT(1),
82697f8a3d1SAyala Barazani .bands = ACPI_GEO_NUM_BANDS_REV0,
82797f8a3d1SAyala Barazani .profiles = ACPI_NUM_GEO_PROFILES,
82897f8a3d1SAyala Barazani },
82997f8a3d1SAyala Barazani };
83097f8a3d1SAyala Barazani int idx;
83197f8a3d1SAyala Barazani /* start from one to skip the domain */
83297f8a3d1SAyala Barazani int entry_idx = 1;
83397f8a3d1SAyala Barazani
83497f8a3d1SAyala Barazani BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES_REV3 != IWL_NUM_GEO_PROFILES_V3);
83597f8a3d1SAyala Barazani BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES != IWL_NUM_GEO_PROFILES);
83639c1a972SIhab Zhaika
83739c1a972SIhab Zhaika data = iwl_acpi_get_object(fwrt->dev, ACPI_WGDS_METHOD);
83839c1a972SIhab Zhaika if (IS_ERR(data))
83939c1a972SIhab Zhaika return PTR_ERR(data);
84039c1a972SIhab Zhaika
84197f8a3d1SAyala Barazani /* read the highest revision we understand first */
84297f8a3d1SAyala Barazani for (idx = 0; idx < ARRAY_SIZE(rev_data); idx++) {
84397f8a3d1SAyala Barazani /* min_profiles != 0 requires num_profiles header */
84497f8a3d1SAyala Barazani u32 hdr_size = 1 + !!rev_data[idx].min_profiles;
84597f8a3d1SAyala Barazani u32 profile_size = ACPI_GEO_PER_CHAIN_SIZE *
84697f8a3d1SAyala Barazani rev_data[idx].bands;
84797f8a3d1SAyala Barazani u32 max_size = hdr_size + profile_size * rev_data[idx].profiles;
84897f8a3d1SAyala Barazani u32 min_size;
84997f8a3d1SAyala Barazani
85097f8a3d1SAyala Barazani if (!rev_data[idx].min_profiles)
85197f8a3d1SAyala Barazani min_size = max_size;
85297f8a3d1SAyala Barazani else
85397f8a3d1SAyala Barazani min_size = hdr_size +
85497f8a3d1SAyala Barazani profile_size * rev_data[idx].min_profiles;
85597f8a3d1SAyala Barazani
85697f8a3d1SAyala Barazani wifi_pkg = iwl_acpi_get_wifi_pkg_range(fwrt->dev, data,
85797f8a3d1SAyala Barazani min_size, max_size,
858664c011bSLuca Coelho &tbl_rev);
859664c011bSLuca Coelho if (!IS_ERR(wifi_pkg)) {
86097f8a3d1SAyala Barazani if (!(BIT(tbl_rev) & rev_data[idx].revisions))
86197f8a3d1SAyala Barazani continue;
86297f8a3d1SAyala Barazani
86397f8a3d1SAyala Barazani num_bands = rev_data[idx].bands;
86497f8a3d1SAyala Barazani num_profiles = rev_data[idx].profiles;
86597f8a3d1SAyala Barazani
86697f8a3d1SAyala Barazani if (rev_data[idx].min_profiles) {
86797f8a3d1SAyala Barazani /* read header that says # of profiles */
86897f8a3d1SAyala Barazani union acpi_object *entry;
86997f8a3d1SAyala Barazani
87097f8a3d1SAyala Barazani entry = &wifi_pkg->package.elements[entry_idx];
87197f8a3d1SAyala Barazani entry_idx++;
87297f8a3d1SAyala Barazani if (entry->type != ACPI_TYPE_INTEGER ||
8731a525d99SAnjaneyulu entry->integer.value > num_profiles ||
8741a525d99SAnjaneyulu entry->integer.value <
8751a525d99SAnjaneyulu rev_data[idx].min_profiles) {
87697f8a3d1SAyala Barazani ret = -EINVAL;
87739c1a972SIhab Zhaika goto out_free;
87839c1a972SIhab Zhaika }
87939c1a972SIhab Zhaika
88097f8a3d1SAyala Barazani /*
8811a525d99SAnjaneyulu * Check to see if we received package count
8821a525d99SAnjaneyulu * same as max # of profiles
88397f8a3d1SAyala Barazani */
88497f8a3d1SAyala Barazani if (wifi_pkg->package.count !=
885dc276ffdSMiri Korenblit hdr_size + profile_size * num_profiles) {
88697f8a3d1SAyala Barazani ret = -EINVAL;
88755ae96b6SHaim Dreyfuss goto out_free;
88855ae96b6SHaim Dreyfuss }
8891a525d99SAnjaneyulu
8901a525d99SAnjaneyulu /* Number of valid profiles */
8911a525d99SAnjaneyulu num_profiles = entry->integer.value;
89297f8a3d1SAyala Barazani }
893664c011bSLuca Coelho goto read_table;
894664c011bSLuca Coelho }
89597f8a3d1SAyala Barazani }
896664c011bSLuca Coelho
89797f8a3d1SAyala Barazani if (idx < ARRAY_SIZE(rev_data))
898664c011bSLuca Coelho ret = PTR_ERR(wifi_pkg);
89997f8a3d1SAyala Barazani else
90097f8a3d1SAyala Barazani ret = -ENOENT;
901664c011bSLuca Coelho goto out_free;
902664c011bSLuca Coelho
903664c011bSLuca Coelho read_table:
90439c1a972SIhab Zhaika fwrt->geo_rev = tbl_rev;
90597f8a3d1SAyala Barazani for (i = 0; i < num_profiles; i++) {
906c5b42c67SLuca Coelho for (j = 0; j < ACPI_GEO_NUM_BANDS_REV2; j++) {
90739c1a972SIhab Zhaika union acpi_object *entry;
90839c1a972SIhab Zhaika
909c5b42c67SLuca Coelho /*
910c5b42c67SLuca Coelho * num_bands is either 2 or 3, if it's only 2 then
911c5b42c67SLuca Coelho * fill the third band (6 GHz) with the values from
912c5b42c67SLuca Coelho * 5 GHz (second band)
913c5b42c67SLuca Coelho */
914c5b42c67SLuca Coelho if (j >= num_bands) {
915c5b42c67SLuca Coelho fwrt->geo_profiles[i].bands[j].max =
916c5b42c67SLuca Coelho fwrt->geo_profiles[i].bands[1].max;
917c5b42c67SLuca Coelho } else {
91897f8a3d1SAyala Barazani entry = &wifi_pkg->package.elements[entry_idx];
91997f8a3d1SAyala Barazani entry_idx++;
92039c1a972SIhab Zhaika if (entry->type != ACPI_TYPE_INTEGER ||
92139c1a972SIhab Zhaika entry->integer.value > U8_MAX) {
92239c1a972SIhab Zhaika ret = -EINVAL;
92339c1a972SIhab Zhaika goto out_free;
92439c1a972SIhab Zhaika }
92539c1a972SIhab Zhaika
9265bf7a9edSLuca Coelho fwrt->geo_profiles[i].bands[j].max =
9275bf7a9edSLuca Coelho entry->integer.value;
928c5b42c67SLuca Coelho }
9295bf7a9edSLuca Coelho
9305bf7a9edSLuca Coelho for (k = 0; k < ACPI_GEO_NUM_CHAINS; k++) {
931c5b42c67SLuca Coelho /* same here as above */
932c5b42c67SLuca Coelho if (j >= num_bands) {
933c5b42c67SLuca Coelho fwrt->geo_profiles[i].bands[j].chains[k] =
934c5b42c67SLuca Coelho fwrt->geo_profiles[i].bands[1].chains[k];
935c5b42c67SLuca Coelho } else {
93697f8a3d1SAyala Barazani entry = &wifi_pkg->package.elements[entry_idx];
93797f8a3d1SAyala Barazani entry_idx++;
9385bf7a9edSLuca Coelho if (entry->type != ACPI_TYPE_INTEGER ||
9395bf7a9edSLuca Coelho entry->integer.value > U8_MAX) {
9405bf7a9edSLuca Coelho ret = -EINVAL;
9415bf7a9edSLuca Coelho goto out_free;
9425bf7a9edSLuca Coelho }
9435bf7a9edSLuca Coelho
9445bf7a9edSLuca Coelho fwrt->geo_profiles[i].bands[j].chains[k] =
9455bf7a9edSLuca Coelho entry->integer.value;
94639c1a972SIhab Zhaika }
94739c1a972SIhab Zhaika }
9485bf7a9edSLuca Coelho }
949c5b42c67SLuca Coelho }
9505bf7a9edSLuca Coelho
951c593d2faSAyala Barazani fwrt->geo_num_profiles = num_profiles;
952c593d2faSAyala Barazani fwrt->geo_enabled = true;
95339c1a972SIhab Zhaika ret = 0;
95439c1a972SIhab Zhaika out_free:
95539c1a972SIhab Zhaika kfree(data);
95639c1a972SIhab Zhaika return ret;
95739c1a972SIhab Zhaika }
95839c1a972SIhab Zhaika IWL_EXPORT_SYMBOL(iwl_sar_get_wgds_table);
95939c1a972SIhab Zhaika
iwl_sar_geo_support(struct iwl_fw_runtime * fwrt)96039c1a972SIhab Zhaika bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt)
96139c1a972SIhab Zhaika {
96239c1a972SIhab Zhaika /*
963523de6c8SLuca Coelho * The PER_CHAIN_LIMIT_OFFSET_CMD command is not supported on
964523de6c8SLuca Coelho * earlier firmware versions. Unfortunately, we don't have a
965523de6c8SLuca Coelho * TLV API flag to rely on, so rely on the major version which
966523de6c8SLuca Coelho * is in the first byte of ucode_ver. This was implemented
96739c1a972SIhab Zhaika * initially on version 38 and then backported to 17. It was
96839c1a972SIhab Zhaika * also backported to 29, but only for 7265D devices. The
96939c1a972SIhab Zhaika * intention was to have it in 36 as well, but not all 8000
97039c1a972SIhab Zhaika * family got this feature enabled. The 8000 family is the
97139c1a972SIhab Zhaika * only one using version 36, so skip this version entirely.
97239c1a972SIhab Zhaika */
97339c1a972SIhab Zhaika return IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) >= 38 ||
9745f06f6bfSLuca Coelho (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 17 &&
9755f06f6bfSLuca Coelho fwrt->trans->hw_rev != CSR_HW_REV_TYPE_3160) ||
97639c1a972SIhab Zhaika (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 29 &&
97739c1a972SIhab Zhaika ((fwrt->trans->hw_rev & CSR_HW_REV_TYPE_MSK) ==
97839c1a972SIhab Zhaika CSR_HW_REV_TYPE_7265D));
97939c1a972SIhab Zhaika }
98039c1a972SIhab Zhaika IWL_EXPORT_SYMBOL(iwl_sar_geo_support);
98139c1a972SIhab Zhaika
iwl_sar_geo_init(struct iwl_fw_runtime * fwrt,struct iwl_per_chain_offset * table,u32 n_bands,u32 n_profiles)9820433ae55SGolan Ben Ami int iwl_sar_geo_init(struct iwl_fw_runtime *fwrt,
98397f8a3d1SAyala Barazani struct iwl_per_chain_offset *table,
98497f8a3d1SAyala Barazani u32 n_bands, u32 n_profiles)
98539c1a972SIhab Zhaika {
98678a19d52SMiri Korenblit int i, j;
98739c1a972SIhab Zhaika
988d1f6530cSJohannes Berg if (!fwrt->geo_enabled)
989d1f6530cSJohannes Berg return -ENODATA;
990d1f6530cSJohannes Berg
99139c1a972SIhab Zhaika if (!iwl_sar_geo_support(fwrt))
9920433ae55SGolan Ben Ami return -EOPNOTSUPP;
99339c1a972SIhab Zhaika
99497f8a3d1SAyala Barazani for (i = 0; i < n_profiles; i++) {
9950ea788edSLuca Coelho for (j = 0; j < n_bands; j++) {
99645acebf8SNaftali Goldstein struct iwl_per_chain_offset *chain =
99745acebf8SNaftali Goldstein &table[i * n_bands + j];
99839c1a972SIhab Zhaika
9995bf7a9edSLuca Coelho chain->max_tx_power =
10005bf7a9edSLuca Coelho cpu_to_le16(fwrt->geo_profiles[i].bands[j].max);
10015bf7a9edSLuca Coelho chain->chain_a = fwrt->geo_profiles[i].bands[j].chains[0];
10025bf7a9edSLuca Coelho chain->chain_b = fwrt->geo_profiles[i].bands[j].chains[1];
100339c1a972SIhab Zhaika IWL_DEBUG_RADIO(fwrt,
100439c1a972SIhab Zhaika "SAR geographic profile[%d] Band[%d]: chain A = %d chain B = %d max_tx_power = %d\n",
10055bf7a9edSLuca Coelho i, j,
10065bf7a9edSLuca Coelho fwrt->geo_profiles[i].bands[j].chains[0],
10075bf7a9edSLuca Coelho fwrt->geo_profiles[i].bands[j].chains[1],
10085bf7a9edSLuca Coelho fwrt->geo_profiles[i].bands[j].max);
100939c1a972SIhab Zhaika }
101039c1a972SIhab Zhaika }
10110433ae55SGolan Ben Ami
10120433ae55SGolan Ben Ami return 0;
101339c1a972SIhab Zhaika }
101439c1a972SIhab Zhaika IWL_EXPORT_SYMBOL(iwl_sar_geo_init);
1015d2bfda8aSMiri Korenblit
iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime * fwrt)1016f21afabaSHarish Mitty __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt)
1017f21afabaSHarish Mitty {
10187119f02bSMiri Korenblit int ret;
10197119f02bSMiri Korenblit u8 value;
1020f21afabaSHarish Mitty __le32 config_bitmap = 0;
1021f21afabaSHarish Mitty
1022f21afabaSHarish Mitty /*
1023f21afabaSHarish Mitty ** Evaluate func 'DSM_FUNC_ENABLE_INDONESIA_5G2'
1024f21afabaSHarish Mitty */
10257119f02bSMiri Korenblit ret = iwl_acpi_get_dsm_u8(fwrt->dev, 0,
10267119f02bSMiri Korenblit DSM_FUNC_ENABLE_INDONESIA_5G2,
10277119f02bSMiri Korenblit &iwl_guid, &value);
1028f21afabaSHarish Mitty
10297119f02bSMiri Korenblit if (!ret && value == DSM_VALUE_INDONESIA_ENABLE)
1030f21afabaSHarish Mitty config_bitmap |=
1031f21afabaSHarish Mitty cpu_to_le32(LARI_CONFIG_ENABLE_5G2_IN_INDONESIA_MSK);
1032f21afabaSHarish Mitty
1033f21afabaSHarish Mitty /*
1034f21afabaSHarish Mitty ** Evaluate func 'DSM_FUNC_DISABLE_SRD'
1035f21afabaSHarish Mitty */
10367119f02bSMiri Korenblit ret = iwl_acpi_get_dsm_u8(fwrt->dev, 0,
10377119f02bSMiri Korenblit DSM_FUNC_DISABLE_SRD,
10387119f02bSMiri Korenblit &iwl_guid, &value);
10397119f02bSMiri Korenblit if (!ret) {
10407119f02bSMiri Korenblit if (value == DSM_VALUE_SRD_PASSIVE)
1041f21afabaSHarish Mitty config_bitmap |=
1042f21afabaSHarish Mitty cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_PASSIVE_MSK);
10437119f02bSMiri Korenblit else if (value == DSM_VALUE_SRD_DISABLE)
1044f21afabaSHarish Mitty config_bitmap |=
1045f21afabaSHarish Mitty cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_DISABLED_MSK);
10467119f02bSMiri Korenblit }
1047f21afabaSHarish Mitty
1048f21afabaSHarish Mitty return config_bitmap;
1049f21afabaSHarish Mitty }
1050f21afabaSHarish Mitty IWL_EXPORT_SYMBOL(iwl_acpi_get_lari_config_bitmap);
1051e8e10a37SMatt Chen
iwl_acpi_get_ppag_table(struct iwl_fw_runtime * fwrt)1052e8e10a37SMatt Chen int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt)
1053e8e10a37SMatt Chen {
1054e8e10a37SMatt Chen union acpi_object *wifi_pkg, *data, *flags;
1055e8e10a37SMatt Chen int i, j, ret, tbl_rev, num_sub_bands = 0;
1056e8e10a37SMatt Chen int idx = 2;
10571843676aSAlon Giladi u8 cmd_ver;
1058e8e10a37SMatt Chen
1059e8e10a37SMatt Chen fwrt->ppag_flags = 0;
10601843676aSAlon Giladi fwrt->ppag_table_valid = false;
1061e8e10a37SMatt Chen
1062e8e10a37SMatt Chen data = iwl_acpi_get_object(fwrt->dev, ACPI_PPAG_METHOD);
1063e8e10a37SMatt Chen if (IS_ERR(data))
1064e8e10a37SMatt Chen return PTR_ERR(data);
1065e8e10a37SMatt Chen
1066e8e10a37SMatt Chen /* try to read ppag table rev 2 or 1 (both have the same data size) */
1067e8e10a37SMatt Chen wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
1068e8e10a37SMatt Chen ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev);
1069e8e10a37SMatt Chen
1070e8e10a37SMatt Chen if (!IS_ERR(wifi_pkg)) {
1071e8e10a37SMatt Chen if (tbl_rev == 1 || tbl_rev == 2) {
1072e8e10a37SMatt Chen num_sub_bands = IWL_NUM_SUB_BANDS_V2;
1073e8e10a37SMatt Chen IWL_DEBUG_RADIO(fwrt,
1074e8e10a37SMatt Chen "Reading PPAG table v2 (tbl_rev=%d)\n",
1075e8e10a37SMatt Chen tbl_rev);
1076e8e10a37SMatt Chen goto read_table;
1077e8e10a37SMatt Chen } else {
1078e8e10a37SMatt Chen ret = -EINVAL;
1079e8e10a37SMatt Chen goto out_free;
1080e8e10a37SMatt Chen }
1081e8e10a37SMatt Chen }
1082e8e10a37SMatt Chen
1083e8e10a37SMatt Chen /* try to read ppag table revision 0 */
1084e8e10a37SMatt Chen wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
1085e8e10a37SMatt Chen ACPI_PPAG_WIFI_DATA_SIZE_V1, &tbl_rev);
1086e8e10a37SMatt Chen
1087e8e10a37SMatt Chen if (!IS_ERR(wifi_pkg)) {
1088e8e10a37SMatt Chen if (tbl_rev != 0) {
1089e8e10a37SMatt Chen ret = -EINVAL;
1090e8e10a37SMatt Chen goto out_free;
1091e8e10a37SMatt Chen }
1092e8e10a37SMatt Chen num_sub_bands = IWL_NUM_SUB_BANDS_V1;
1093e8e10a37SMatt Chen IWL_DEBUG_RADIO(fwrt, "Reading PPAG table v1 (tbl_rev=0)\n");
1094e8e10a37SMatt Chen goto read_table;
1095e8e10a37SMatt Chen }
1096e8e10a37SMatt Chen
10978723db10SDan Carpenter ret = PTR_ERR(wifi_pkg);
10988723db10SDan Carpenter goto out_free;
10998723db10SDan Carpenter
1100e8e10a37SMatt Chen read_table:
1101e8e10a37SMatt Chen fwrt->ppag_ver = tbl_rev;
1102e8e10a37SMatt Chen flags = &wifi_pkg->package.elements[1];
1103e8e10a37SMatt Chen
1104e8e10a37SMatt Chen if (flags->type != ACPI_TYPE_INTEGER) {
1105e8e10a37SMatt Chen ret = -EINVAL;
1106e8e10a37SMatt Chen goto out_free;
1107e8e10a37SMatt Chen }
1108e8e10a37SMatt Chen
1109e8e10a37SMatt Chen fwrt->ppag_flags = flags->integer.value & ACPI_PPAG_MASK;
11101843676aSAlon Giladi cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw,
11111843676aSAlon Giladi WIDE_ID(PHY_OPS_GROUP,
11121843676aSAlon Giladi PER_PLATFORM_ANT_GAIN_CMD),
11131843676aSAlon Giladi IWL_FW_CMD_VER_UNKNOWN);
11141843676aSAlon Giladi if (cmd_ver == IWL_FW_CMD_VER_UNKNOWN) {
11151843676aSAlon Giladi ret = -EINVAL;
11161843676aSAlon Giladi goto out_free;
11171843676aSAlon Giladi }
11181843676aSAlon Giladi if (!fwrt->ppag_flags && cmd_ver <= 3) {
1119e8e10a37SMatt Chen ret = 0;
1120e8e10a37SMatt Chen goto out_free;
1121e8e10a37SMatt Chen }
1122e8e10a37SMatt Chen
1123e8e10a37SMatt Chen /*
1124e8e10a37SMatt Chen * read, verify gain values and save them into the PPAG table.
1125e8e10a37SMatt Chen * first sub-band (j=0) corresponds to Low-Band (2.4GHz), and the
1126e8e10a37SMatt Chen * following sub-bands to High-Band (5GHz).
1127e8e10a37SMatt Chen */
1128e8e10a37SMatt Chen for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
1129e8e10a37SMatt Chen for (j = 0; j < num_sub_bands; j++) {
1130e8e10a37SMatt Chen union acpi_object *ent;
1131e8e10a37SMatt Chen
1132e8e10a37SMatt Chen ent = &wifi_pkg->package.elements[idx++];
1133e8e10a37SMatt Chen if (ent->type != ACPI_TYPE_INTEGER) {
1134e8e10a37SMatt Chen ret = -EINVAL;
1135e8e10a37SMatt Chen goto out_free;
1136e8e10a37SMatt Chen }
1137e8e10a37SMatt Chen
1138e8e10a37SMatt Chen fwrt->ppag_chains[i].subbands[j] = ent->integer.value;
11391843676aSAlon Giladi /* from ver 4 the fw deals with out of range values */
11401843676aSAlon Giladi if (cmd_ver >= 4)
11411843676aSAlon Giladi continue;
1142e8e10a37SMatt Chen if ((j == 0 &&
1143e8e10a37SMatt Chen (fwrt->ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_LB ||
1144e8e10a37SMatt Chen fwrt->ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_LB)) ||
1145e8e10a37SMatt Chen (j != 0 &&
1146e8e10a37SMatt Chen (fwrt->ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_HB ||
1147e8e10a37SMatt Chen fwrt->ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_HB))) {
1148e8e10a37SMatt Chen ret = -EINVAL;
1149e8e10a37SMatt Chen goto out_free;
1150e8e10a37SMatt Chen }
1151e8e10a37SMatt Chen }
1152e8e10a37SMatt Chen }
1153e8e10a37SMatt Chen
11541843676aSAlon Giladi fwrt->ppag_table_valid = true;
1155e8e10a37SMatt Chen ret = 0;
1156e8e10a37SMatt Chen
1157e8e10a37SMatt Chen out_free:
1158e8e10a37SMatt Chen kfree(data);
1159e8e10a37SMatt Chen return ret;
1160e8e10a37SMatt Chen }
1161e8e10a37SMatt Chen IWL_EXPORT_SYMBOL(iwl_acpi_get_ppag_table);
1162e8e10a37SMatt Chen
iwl_read_ppag_table(struct iwl_fw_runtime * fwrt,union iwl_ppag_table_cmd * cmd,int * cmd_size)1163e8e10a37SMatt Chen int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *cmd,
1164e8e10a37SMatt Chen int *cmd_size)
1165e8e10a37SMatt Chen {
1166e8e10a37SMatt Chen u8 cmd_ver;
1167e8e10a37SMatt Chen int i, j, num_sub_bands;
1168e8e10a37SMatt Chen s8 *gain;
1169e8e10a37SMatt Chen
11701c4c0b28SJohannes Berg /* many firmware images for JF lie about this */
11711c4c0b28SJohannes Berg if (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id) ==
11721c4c0b28SJohannes Berg CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF))
11731c4c0b28SJohannes Berg return -EOPNOTSUPP;
11741c4c0b28SJohannes Berg
1175e8e10a37SMatt Chen if (!fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) {
1176e8e10a37SMatt Chen IWL_DEBUG_RADIO(fwrt,
1177e8e10a37SMatt Chen "PPAG capability not supported by FW, command not sent.\n");
1178e8e10a37SMatt Chen return -EINVAL;
1179e8e10a37SMatt Chen }
11801843676aSAlon Giladi
11811843676aSAlon Giladi cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw,
11821843676aSAlon Giladi WIDE_ID(PHY_OPS_GROUP,
11831843676aSAlon Giladi PER_PLATFORM_ANT_GAIN_CMD),
11841843676aSAlon Giladi IWL_FW_CMD_VER_UNKNOWN);
11851843676aSAlon Giladi if (!fwrt->ppag_table_valid || (cmd_ver <= 3 && !fwrt->ppag_flags)) {
1186e8e10a37SMatt Chen IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n");
1187e8e10a37SMatt Chen return -EINVAL;
1188e8e10a37SMatt Chen }
1189e8e10a37SMatt Chen
1190e8e10a37SMatt Chen /* The 'flags' field is the same in v1 and in v2 so we can just
1191e8e10a37SMatt Chen * use v1 to access it.
1192e8e10a37SMatt Chen */
1193e8e10a37SMatt Chen cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags);
11941843676aSAlon Giladi
1195ff75c21cSAlon Giladi IWL_DEBUG_RADIO(fwrt, "PPAG cmd ver is %d\n", cmd_ver);
1196e8e10a37SMatt Chen if (cmd_ver == 1) {
1197e8e10a37SMatt Chen num_sub_bands = IWL_NUM_SUB_BANDS_V1;
1198e8e10a37SMatt Chen gain = cmd->v1.gain[0];
1199e8e10a37SMatt Chen *cmd_size = sizeof(cmd->v1);
1200e8e10a37SMatt Chen if (fwrt->ppag_ver == 1 || fwrt->ppag_ver == 2) {
1201ff75c21cSAlon Giladi /* in this case FW supports revision 0 */
1202e8e10a37SMatt Chen IWL_DEBUG_RADIO(fwrt,
1203ff75c21cSAlon Giladi "PPAG table rev is %d, send truncated table\n",
1204e8e10a37SMatt Chen fwrt->ppag_ver);
1205e8e10a37SMatt Chen }
12061843676aSAlon Giladi } else if (cmd_ver >= 2 && cmd_ver <= 4) {
1207e8e10a37SMatt Chen num_sub_bands = IWL_NUM_SUB_BANDS_V2;
1208e8e10a37SMatt Chen gain = cmd->v2.gain[0];
1209e8e10a37SMatt Chen *cmd_size = sizeof(cmd->v2);
1210e8e10a37SMatt Chen if (fwrt->ppag_ver == 0) {
1211ff75c21cSAlon Giladi /* in this case FW supports revisions 1 or 2 */
1212e8e10a37SMatt Chen IWL_DEBUG_RADIO(fwrt,
1213ff75c21cSAlon Giladi "PPAG table rev is 0, send padded table\n");
1214e8e10a37SMatt Chen }
1215e8e10a37SMatt Chen } else {
1216e8e10a37SMatt Chen IWL_DEBUG_RADIO(fwrt, "Unsupported PPAG command version\n");
1217e8e10a37SMatt Chen return -EINVAL;
1218e8e10a37SMatt Chen }
1219e8e10a37SMatt Chen
1220ff75c21cSAlon Giladi /* ppag mode */
1221ff75c21cSAlon Giladi IWL_DEBUG_RADIO(fwrt,
1222ff75c21cSAlon Giladi "PPAG MODE bits were read from bios: %d\n",
1223ff75c21cSAlon Giladi cmd->v1.flags & cpu_to_le32(ACPI_PPAG_MASK));
1224ff75c21cSAlon Giladi if ((cmd_ver == 1 && !fw_has_capa(&fwrt->fw->ucode_capa,
1225ff75c21cSAlon Giladi IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) ||
1226ff75c21cSAlon Giladi (cmd_ver == 2 && fwrt->ppag_ver == 2)) {
1227ff75c21cSAlon Giladi cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK);
1228ff75c21cSAlon Giladi IWL_DEBUG_RADIO(fwrt, "masking ppag China bit\n");
1229ff75c21cSAlon Giladi } else {
1230ff75c21cSAlon Giladi IWL_DEBUG_RADIO(fwrt, "isn't masking ppag China bit\n");
1231ff75c21cSAlon Giladi }
1232ff75c21cSAlon Giladi
1233ff75c21cSAlon Giladi IWL_DEBUG_RADIO(fwrt,
1234ff75c21cSAlon Giladi "PPAG MODE bits going to be sent: %d\n",
1235ff75c21cSAlon Giladi cmd->v1.flags & cpu_to_le32(ACPI_PPAG_MASK));
1236ff75c21cSAlon Giladi
1237e8e10a37SMatt Chen for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
1238e8e10a37SMatt Chen for (j = 0; j < num_sub_bands; j++) {
1239e8e10a37SMatt Chen gain[i * num_sub_bands + j] =
1240e8e10a37SMatt Chen fwrt->ppag_chains[i].subbands[j];
1241e8e10a37SMatt Chen IWL_DEBUG_RADIO(fwrt,
1242e8e10a37SMatt Chen "PPAG table: chain[%d] band[%d]: gain = %d\n",
1243e8e10a37SMatt Chen i, j, gain[i * num_sub_bands + j]);
1244e8e10a37SMatt Chen }
1245e8e10a37SMatt Chen }
1246e8e10a37SMatt Chen
1247e8e10a37SMatt Chen return 0;
1248e8e10a37SMatt Chen }
1249e8e10a37SMatt Chen IWL_EXPORT_SYMBOL(iwl_read_ppag_table);
1250e8e10a37SMatt Chen
iwl_acpi_is_ppag_approved(struct iwl_fw_runtime * fwrt)1251e8e10a37SMatt Chen bool iwl_acpi_is_ppag_approved(struct iwl_fw_runtime *fwrt)
1252e8e10a37SMatt Chen {
1253e8e10a37SMatt Chen
1254e8e10a37SMatt Chen if (!dmi_check_system(dmi_ppag_approved_list)) {
1255e8e10a37SMatt Chen IWL_DEBUG_RADIO(fwrt,
1256e8e10a37SMatt Chen "System vendor '%s' is not in the approved list, disabling PPAG.\n",
1257e8e10a37SMatt Chen dmi_get_system_info(DMI_SYS_VENDOR));
1258e8e10a37SMatt Chen fwrt->ppag_flags = 0;
1259e8e10a37SMatt Chen return false;
1260e8e10a37SMatt Chen }
1261e8e10a37SMatt Chen
1262e8e10a37SMatt Chen return true;
1263e8e10a37SMatt Chen }
1264e8e10a37SMatt Chen IWL_EXPORT_SYMBOL(iwl_acpi_is_ppag_approved);
1265c4c95454SJohannes Berg
iwl_acpi_get_phy_filters(struct iwl_fw_runtime * fwrt,struct iwl_phy_specific_cfg * filters)1266c4c95454SJohannes Berg void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt,
1267c4c95454SJohannes Berg struct iwl_phy_specific_cfg *filters)
1268c4c95454SJohannes Berg {
1269c4c95454SJohannes Berg struct iwl_phy_specific_cfg tmp = {};
1270c4c95454SJohannes Berg union acpi_object *wifi_pkg, *data;
1271c4c95454SJohannes Berg int tbl_rev, i;
1272c4c95454SJohannes Berg
1273c4c95454SJohannes Berg data = iwl_acpi_get_object(fwrt->dev, ACPI_WPFC_METHOD);
1274c4c95454SJohannes Berg if (IS_ERR(data))
1275c4c95454SJohannes Berg return;
1276c4c95454SJohannes Berg
1277c4c95454SJohannes Berg wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
1278c4c95454SJohannes Berg ACPI_WPFC_WIFI_DATA_SIZE,
1279c4c95454SJohannes Berg &tbl_rev);
1280c4c95454SJohannes Berg if (IS_ERR(wifi_pkg))
1281c4c95454SJohannes Berg goto out_free;
1282c4c95454SJohannes Berg
1283c4c95454SJohannes Berg if (tbl_rev != 0)
1284c4c95454SJohannes Berg goto out_free;
1285c4c95454SJohannes Berg
128645eacd73SJohannes Berg BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) !=
128745eacd73SJohannes Berg ACPI_WPFC_WIFI_DATA_SIZE - 1);
1288c4c95454SJohannes Berg
1289c4c95454SJohannes Berg for (i = 0; i < ARRAY_SIZE(filters->filter_cfg_chains); i++) {
129045eacd73SJohannes Berg if (wifi_pkg->package.elements[i + 1].type != ACPI_TYPE_INTEGER)
129145eacd73SJohannes Berg goto out_free;
1292c4c95454SJohannes Berg tmp.filter_cfg_chains[i] =
129345eacd73SJohannes Berg cpu_to_le32(wifi_pkg->package.elements[i + 1].integer.value);
1294c4c95454SJohannes Berg }
1295c4c95454SJohannes Berg
1296c4c95454SJohannes Berg IWL_DEBUG_RADIO(fwrt, "Loaded WPFC filter config from ACPI\n");
1297c4c95454SJohannes Berg *filters = tmp;
1298c4c95454SJohannes Berg out_free:
1299c4c95454SJohannes Berg kfree(data);
1300c4c95454SJohannes Berg }
1301c4c95454SJohannes Berg IWL_EXPORT_SYMBOL(iwl_acpi_get_phy_filters);
1302