0001
0002
0003
0004
0005
0006 #include <linux/uuid.h>
0007 #include <linux/dmi.h>
0008 #include "iwl-drv.h"
0009 #include "iwl-debug.h"
0010 #include "acpi.h"
0011 #include "fw/runtime.h"
0012
0013 const guid_t iwl_guid = GUID_INIT(0xF21202BF, 0x8F78, 0x4DC6,
0014 0xA5, 0xB3, 0x1F, 0x73,
0015 0x8E, 0x28, 0x5A, 0xDE);
0016 IWL_EXPORT_SYMBOL(iwl_guid);
0017
0018 const guid_t iwl_rfi_guid = GUID_INIT(0x7266172C, 0x220B, 0x4B29,
0019 0x81, 0x4F, 0x75, 0xE4,
0020 0xDD, 0x26, 0xB5, 0xFD);
0021 IWL_EXPORT_SYMBOL(iwl_rfi_guid);
0022
0023 static const struct dmi_system_id dmi_ppag_approved_list[] = {
0024 { .ident = "HP",
0025 .matches = {
0026 DMI_MATCH(DMI_SYS_VENDOR, "HP"),
0027 },
0028 },
0029 { .ident = "SAMSUNG",
0030 .matches = {
0031 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"),
0032 },
0033 },
0034 { .ident = "MSFT",
0035 .matches = {
0036 DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
0037 },
0038 },
0039 { .ident = "ASUS",
0040 .matches = {
0041 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTek COMPUTER INC."),
0042 },
0043 },
0044 {}
0045 };
0046
0047 static int iwl_acpi_get_handle(struct device *dev, acpi_string method,
0048 acpi_handle *ret_handle)
0049 {
0050 acpi_handle root_handle;
0051 acpi_status status;
0052
0053 root_handle = ACPI_HANDLE(dev);
0054 if (!root_handle) {
0055 IWL_DEBUG_DEV_RADIO(dev,
0056 "ACPI: Could not retrieve root port handle\n");
0057 return -ENOENT;
0058 }
0059
0060 status = acpi_get_handle(root_handle, method, ret_handle);
0061 if (ACPI_FAILURE(status)) {
0062 IWL_DEBUG_DEV_RADIO(dev,
0063 "ACPI: %s method not found\n", method);
0064 return -ENOENT;
0065 }
0066 return 0;
0067 }
0068
0069 void *iwl_acpi_get_object(struct device *dev, acpi_string method)
0070 {
0071 struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
0072 acpi_handle handle;
0073 acpi_status status;
0074 int ret;
0075
0076 ret = iwl_acpi_get_handle(dev, method, &handle);
0077 if (ret)
0078 return ERR_PTR(-ENOENT);
0079
0080
0081 status = acpi_evaluate_object(handle, NULL, NULL, &buf);
0082 if (ACPI_FAILURE(status)) {
0083 IWL_DEBUG_DEV_RADIO(dev,
0084 "ACPI: %s method invocation failed (status: 0x%x)\n",
0085 method, status);
0086 return ERR_PTR(-ENOENT);
0087 }
0088 return buf.pointer;
0089 }
0090 IWL_EXPORT_SYMBOL(iwl_acpi_get_object);
0091
0092
0093
0094
0095
0096
0097 static void *iwl_acpi_get_dsm_object(struct device *dev, int rev, int func,
0098 union acpi_object *args,
0099 const guid_t *guid)
0100 {
0101 union acpi_object *obj;
0102
0103 obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), guid, rev, func,
0104 args);
0105 if (!obj) {
0106 IWL_DEBUG_DEV_RADIO(dev,
0107 "ACPI: DSM method invocation failed (rev: %d, func:%d)\n",
0108 rev, func);
0109 return ERR_PTR(-ENOENT);
0110 }
0111 return obj;
0112 }
0113
0114
0115
0116
0117
0118
0119
0120
0121 static int iwl_acpi_get_dsm_integer(struct device *dev, int rev, int func,
0122 const guid_t *guid, u64 *value,
0123 size_t expected_size)
0124 {
0125 union acpi_object *obj;
0126 int ret = 0;
0127
0128 obj = iwl_acpi_get_dsm_object(dev, rev, func, NULL, guid);
0129 if (IS_ERR(obj)) {
0130 IWL_DEBUG_DEV_RADIO(dev,
0131 "Failed to get DSM object. func= %d\n",
0132 func);
0133 return -ENOENT;
0134 }
0135
0136 if (obj->type == ACPI_TYPE_INTEGER) {
0137 *value = obj->integer.value;
0138 } else if (obj->type == ACPI_TYPE_BUFFER) {
0139 __le64 le_value = 0;
0140
0141 if (WARN_ON_ONCE(expected_size > sizeof(le_value)))
0142 return -EINVAL;
0143
0144
0145 if (obj->buffer.length != expected_size)
0146 IWL_DEBUG_DEV_RADIO(dev,
0147 "ACPI: DSM invalid buffer size, padding or truncating (%d)\n",
0148 obj->buffer.length);
0149
0150
0151 memcpy(&le_value, obj->buffer.pointer,
0152 min_t(size_t, expected_size, (size_t)obj->buffer.length));
0153 *value = le64_to_cpu(le_value);
0154 } else {
0155 IWL_DEBUG_DEV_RADIO(dev,
0156 "ACPI: DSM method did not return a valid object, type=%d\n",
0157 obj->type);
0158 ret = -EINVAL;
0159 goto out;
0160 }
0161
0162 IWL_DEBUG_DEV_RADIO(dev,
0163 "ACPI: DSM method evaluated: func=%d, ret=%d\n",
0164 func, ret);
0165 out:
0166 ACPI_FREE(obj);
0167 return ret;
0168 }
0169
0170
0171
0172
0173 int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func,
0174 const guid_t *guid, u8 *value)
0175 {
0176 int ret;
0177 u64 val;
0178
0179 ret = iwl_acpi_get_dsm_integer(dev, rev, func,
0180 guid, &val, sizeof(u8));
0181
0182 if (ret < 0)
0183 return ret;
0184
0185
0186 *value = (u8)val;
0187 return 0;
0188 }
0189 IWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u8);
0190
0191
0192
0193
0194 int iwl_acpi_get_dsm_u32(struct device *dev, int rev, int func,
0195 const guid_t *guid, u32 *value)
0196 {
0197 int ret;
0198 u64 val;
0199
0200 ret = iwl_acpi_get_dsm_integer(dev, rev, func,
0201 guid, &val, sizeof(u32));
0202
0203 if (ret < 0)
0204 return ret;
0205
0206
0207 *value = (u32)val;
0208 return 0;
0209 }
0210 IWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u32);
0211
0212 union acpi_object *iwl_acpi_get_wifi_pkg_range(struct device *dev,
0213 union acpi_object *data,
0214 int min_data_size,
0215 int max_data_size,
0216 int *tbl_rev)
0217 {
0218 int i;
0219 union acpi_object *wifi_pkg;
0220
0221
0222
0223
0224
0225
0226 if (WARN_ON_ONCE(min_data_size < 2 || min_data_size > max_data_size))
0227 return ERR_PTR(-EINVAL);
0228
0229
0230
0231
0232
0233
0234
0235 if (data->type != ACPI_TYPE_PACKAGE ||
0236 data->package.count < 2 ||
0237 data->package.elements[0].type != ACPI_TYPE_INTEGER) {
0238 IWL_DEBUG_DEV_RADIO(dev, "Invalid packages structure\n");
0239 return ERR_PTR(-EINVAL);
0240 }
0241
0242 *tbl_rev = data->package.elements[0].integer.value;
0243
0244
0245 for (i = 1; i < data->package.count; i++) {
0246 union acpi_object *domain;
0247
0248 wifi_pkg = &data->package.elements[i];
0249
0250
0251 if (wifi_pkg->type != ACPI_TYPE_PACKAGE ||
0252 wifi_pkg->package.count < min_data_size ||
0253 wifi_pkg->package.count > max_data_size)
0254 continue;
0255
0256 domain = &wifi_pkg->package.elements[0];
0257 if (domain->type == ACPI_TYPE_INTEGER &&
0258 domain->integer.value == ACPI_WIFI_DOMAIN)
0259 goto found;
0260 }
0261
0262 return ERR_PTR(-ENOENT);
0263
0264 found:
0265 return wifi_pkg;
0266 }
0267 IWL_EXPORT_SYMBOL(iwl_acpi_get_wifi_pkg_range);
0268
0269 int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt,
0270 union iwl_tas_config_cmd *cmd, int fw_ver)
0271 {
0272 union acpi_object *wifi_pkg, *data;
0273 int ret, tbl_rev, i, block_list_size, enabled;
0274
0275 data = iwl_acpi_get_object(fwrt->dev, ACPI_WTAS_METHOD);
0276 if (IS_ERR(data))
0277 return PTR_ERR(data);
0278
0279
0280 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
0281 ACPI_WTAS_WIFI_DATA_SIZE,
0282 &tbl_rev);
0283 if (IS_ERR(wifi_pkg)) {
0284 ret = PTR_ERR(wifi_pkg);
0285 goto out_free;
0286 }
0287
0288 if (tbl_rev == 1 && wifi_pkg->package.elements[1].type ==
0289 ACPI_TYPE_INTEGER) {
0290 u32 tas_selection =
0291 (u32)wifi_pkg->package.elements[1].integer.value;
0292 u16 override_iec =
0293 (tas_selection & ACPI_WTAS_OVERRIDE_IEC_MSK) >> ACPI_WTAS_OVERRIDE_IEC_POS;
0294 u16 enabled_iec = (tas_selection & ACPI_WTAS_ENABLE_IEC_MSK) >>
0295 ACPI_WTAS_ENABLE_IEC_POS;
0296 u8 usa_tas_uhb = (tas_selection & ACPI_WTAS_USA_UHB_MSK) >> ACPI_WTAS_USA_UHB_POS;
0297
0298
0299 enabled = tas_selection & ACPI_WTAS_ENABLED_MSK;
0300 if (fw_ver <= 3) {
0301 cmd->v3.override_tas_iec = cpu_to_le16(override_iec);
0302 cmd->v3.enable_tas_iec = cpu_to_le16(enabled_iec);
0303 } else {
0304 cmd->v4.usa_tas_uhb_allowed = usa_tas_uhb;
0305 cmd->v4.override_tas_iec = (u8)override_iec;
0306 cmd->v4.enable_tas_iec = (u8)enabled_iec;
0307 }
0308
0309 } else if (tbl_rev == 0 &&
0310 wifi_pkg->package.elements[1].type == ACPI_TYPE_INTEGER) {
0311 enabled = !!wifi_pkg->package.elements[1].integer.value;
0312 } else {
0313 ret = -EINVAL;
0314 goto out_free;
0315 }
0316
0317 if (!enabled) {
0318 IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n");
0319 ret = 0;
0320 goto out_free;
0321 }
0322
0323 IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", tbl_rev);
0324 if (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER ||
0325 wifi_pkg->package.elements[2].integer.value >
0326 APCI_WTAS_BLACK_LIST_MAX) {
0327 IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %llu\n",
0328 wifi_pkg->package.elements[2].integer.value);
0329 ret = -EINVAL;
0330 goto out_free;
0331 }
0332 block_list_size = wifi_pkg->package.elements[2].integer.value;
0333 cmd->v4.block_list_size = cpu_to_le32(block_list_size);
0334
0335 IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", block_list_size);
0336 if (block_list_size > APCI_WTAS_BLACK_LIST_MAX) {
0337 IWL_DEBUG_RADIO(fwrt, "TAS invalid array size value %u\n",
0338 block_list_size);
0339 ret = -EINVAL;
0340 goto out_free;
0341 }
0342
0343 for (i = 0; i < block_list_size; i++) {
0344 u32 country;
0345
0346 if (wifi_pkg->package.elements[3 + i].type !=
0347 ACPI_TYPE_INTEGER) {
0348 IWL_DEBUG_RADIO(fwrt,
0349 "TAS invalid array elem %d\n", 3 + i);
0350 ret = -EINVAL;
0351 goto out_free;
0352 }
0353
0354 country = wifi_pkg->package.elements[3 + i].integer.value;
0355 cmd->v4.block_list_array[i] = cpu_to_le32(country);
0356 IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country);
0357 }
0358
0359 ret = 1;
0360 out_free:
0361 kfree(data);
0362 return ret;
0363 }
0364 IWL_EXPORT_SYMBOL(iwl_acpi_get_tas);
0365
0366 int iwl_acpi_get_mcc(struct device *dev, char *mcc)
0367 {
0368 union acpi_object *wifi_pkg, *data;
0369 u32 mcc_val;
0370 int ret, tbl_rev;
0371
0372 data = iwl_acpi_get_object(dev, ACPI_WRDD_METHOD);
0373 if (IS_ERR(data))
0374 return PTR_ERR(data);
0375
0376 wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE,
0377 &tbl_rev);
0378 if (IS_ERR(wifi_pkg)) {
0379 ret = PTR_ERR(wifi_pkg);
0380 goto out_free;
0381 }
0382
0383 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
0384 tbl_rev != 0) {
0385 ret = -EINVAL;
0386 goto out_free;
0387 }
0388
0389 mcc_val = wifi_pkg->package.elements[1].integer.value;
0390
0391 mcc[0] = (mcc_val >> 8) & 0xff;
0392 mcc[1] = mcc_val & 0xff;
0393 mcc[2] = '\0';
0394
0395 ret = 0;
0396 out_free:
0397 kfree(data);
0398 return ret;
0399 }
0400 IWL_EXPORT_SYMBOL(iwl_acpi_get_mcc);
0401
0402 u64 iwl_acpi_get_pwr_limit(struct device *dev)
0403 {
0404 union acpi_object *data, *wifi_pkg;
0405 u64 dflt_pwr_limit;
0406 int tbl_rev;
0407
0408 data = iwl_acpi_get_object(dev, ACPI_SPLC_METHOD);
0409 if (IS_ERR(data)) {
0410 dflt_pwr_limit = 0;
0411 goto out;
0412 }
0413
0414 wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data,
0415 ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev);
0416 if (IS_ERR(wifi_pkg) || tbl_rev != 0 ||
0417 wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) {
0418 dflt_pwr_limit = 0;
0419 goto out_free;
0420 }
0421
0422 dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value;
0423 out_free:
0424 kfree(data);
0425 out:
0426 return dflt_pwr_limit;
0427 }
0428 IWL_EXPORT_SYMBOL(iwl_acpi_get_pwr_limit);
0429
0430 int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk)
0431 {
0432 union acpi_object *wifi_pkg, *data;
0433 int ret, tbl_rev;
0434
0435 data = iwl_acpi_get_object(dev, ACPI_ECKV_METHOD);
0436 if (IS_ERR(data))
0437 return PTR_ERR(data);
0438
0439 wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_ECKV_WIFI_DATA_SIZE,
0440 &tbl_rev);
0441 if (IS_ERR(wifi_pkg)) {
0442 ret = PTR_ERR(wifi_pkg);
0443 goto out_free;
0444 }
0445
0446 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
0447 tbl_rev != 0) {
0448 ret = -EINVAL;
0449 goto out_free;
0450 }
0451
0452 *extl_clk = wifi_pkg->package.elements[1].integer.value;
0453
0454 ret = 0;
0455
0456 out_free:
0457 kfree(data);
0458 return ret;
0459 }
0460 IWL_EXPORT_SYMBOL(iwl_acpi_get_eckv);
0461
0462 static int iwl_sar_set_profile(union acpi_object *table,
0463 struct iwl_sar_profile *profile,
0464 bool enabled, u8 num_chains, u8 num_sub_bands)
0465 {
0466 int i, j, idx = 0;
0467
0468
0469
0470
0471
0472 for (i = 0; i < ACPI_SAR_NUM_CHAINS_REV2; i++) {
0473 for (j = 0; j < ACPI_SAR_NUM_SUB_BANDS_REV2; j++) {
0474
0475 if (i >= num_chains || j >= num_sub_bands) {
0476 profile->chains[i].subbands[j] = 0;
0477 } else {
0478 if (table[idx].type != ACPI_TYPE_INTEGER ||
0479 table[idx].integer.value > U8_MAX)
0480 return -EINVAL;
0481
0482 profile->chains[i].subbands[j] =
0483 table[idx].integer.value;
0484
0485 idx++;
0486 }
0487 }
0488 }
0489
0490
0491 profile->enabled = enabled;
0492
0493 return 0;
0494 }
0495
0496 static int iwl_sar_fill_table(struct iwl_fw_runtime *fwrt,
0497 __le16 *per_chain, u32 n_subbands,
0498 int prof_a, int prof_b)
0499 {
0500 int profs[ACPI_SAR_NUM_CHAINS_REV0] = { prof_a, prof_b };
0501 int i, j;
0502
0503 for (i = 0; i < ACPI_SAR_NUM_CHAINS_REV0; i++) {
0504 struct iwl_sar_profile *prof;
0505
0506
0507 if (profs[i] == 0)
0508 return -EPERM;
0509
0510
0511 if (profs[i] > ACPI_SAR_PROFILE_NUM)
0512 return -EINVAL;
0513
0514
0515 prof = &fwrt->sar_profiles[profs[i] - 1];
0516
0517
0518 if (!prof->enabled) {
0519 IWL_DEBUG_RADIO(fwrt, "SAR profile %d is disabled.\n",
0520 profs[i]);
0521
0522
0523
0524
0525
0526 return 1;
0527 }
0528
0529 IWL_DEBUG_INFO(fwrt,
0530 "SAR EWRD: chain %d profile index %d\n",
0531 i, profs[i]);
0532 IWL_DEBUG_RADIO(fwrt, " Chain[%d]:\n", i);
0533 for (j = 0; j < n_subbands; j++) {
0534 per_chain[i * n_subbands + j] =
0535 cpu_to_le16(prof->chains[i].subbands[j]);
0536 IWL_DEBUG_RADIO(fwrt, " Band[%d] = %d * .125dBm\n",
0537 j, prof->chains[i].subbands[j]);
0538 }
0539 }
0540
0541 return 0;
0542 }
0543
0544 int iwl_sar_select_profile(struct iwl_fw_runtime *fwrt,
0545 __le16 *per_chain, u32 n_tables, u32 n_subbands,
0546 int prof_a, int prof_b)
0547 {
0548 int i, ret = 0;
0549
0550 for (i = 0; i < n_tables; i++) {
0551 ret = iwl_sar_fill_table(fwrt,
0552 &per_chain[i * n_subbands * ACPI_SAR_NUM_CHAINS_REV0],
0553 n_subbands, prof_a, prof_b);
0554 if (ret)
0555 break;
0556 }
0557
0558 return ret;
0559 }
0560 IWL_EXPORT_SYMBOL(iwl_sar_select_profile);
0561
0562 int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt)
0563 {
0564 union acpi_object *wifi_pkg, *table, *data;
0565 int ret, tbl_rev;
0566 u32 flags;
0567 u8 num_chains, num_sub_bands;
0568
0569 data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDS_METHOD);
0570 if (IS_ERR(data))
0571 return PTR_ERR(data);
0572
0573
0574 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
0575 ACPI_WRDS_WIFI_DATA_SIZE_REV2,
0576 &tbl_rev);
0577 if (!IS_ERR(wifi_pkg)) {
0578 if (tbl_rev != 2) {
0579 ret = PTR_ERR(wifi_pkg);
0580 goto out_free;
0581 }
0582
0583 num_chains = ACPI_SAR_NUM_CHAINS_REV2;
0584 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;
0585
0586 goto read_table;
0587 }
0588
0589
0590 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
0591 ACPI_WRDS_WIFI_DATA_SIZE_REV1,
0592 &tbl_rev);
0593 if (!IS_ERR(wifi_pkg)) {
0594 if (tbl_rev != 1) {
0595 ret = PTR_ERR(wifi_pkg);
0596 goto out_free;
0597 }
0598
0599 num_chains = ACPI_SAR_NUM_CHAINS_REV1;
0600 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;
0601
0602 goto read_table;
0603 }
0604
0605
0606 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
0607 ACPI_WRDS_WIFI_DATA_SIZE_REV0,
0608 &tbl_rev);
0609 if (!IS_ERR(wifi_pkg)) {
0610 if (tbl_rev != 0) {
0611 ret = PTR_ERR(wifi_pkg);
0612 goto out_free;
0613 }
0614
0615 num_chains = ACPI_SAR_NUM_CHAINS_REV0;
0616 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;
0617
0618 goto read_table;
0619 }
0620
0621 ret = PTR_ERR(wifi_pkg);
0622 goto out_free;
0623
0624 read_table:
0625 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
0626 ret = -EINVAL;
0627 goto out_free;
0628 }
0629
0630 IWL_DEBUG_RADIO(fwrt, "Reading WRDS tbl_rev=%d\n", tbl_rev);
0631
0632 flags = wifi_pkg->package.elements[1].integer.value;
0633 fwrt->reduced_power_flags = flags >> IWL_REDUCE_POWER_FLAGS_POS;
0634
0635
0636 table = &wifi_pkg->package.elements[2];
0637
0638
0639
0640
0641 ret = iwl_sar_set_profile(table, &fwrt->sar_profiles[0],
0642 flags & IWL_SAR_ENABLE_MSK,
0643 num_chains, num_sub_bands);
0644 out_free:
0645 kfree(data);
0646 return ret;
0647 }
0648 IWL_EXPORT_SYMBOL(iwl_sar_get_wrds_table);
0649
0650 int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt)
0651 {
0652 union acpi_object *wifi_pkg, *data;
0653 bool enabled;
0654 int i, n_profiles, tbl_rev, pos;
0655 int ret = 0;
0656 u8 num_chains, num_sub_bands;
0657
0658 data = iwl_acpi_get_object(fwrt->dev, ACPI_EWRD_METHOD);
0659 if (IS_ERR(data))
0660 return PTR_ERR(data);
0661
0662
0663 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
0664 ACPI_EWRD_WIFI_DATA_SIZE_REV2,
0665 &tbl_rev);
0666 if (!IS_ERR(wifi_pkg)) {
0667 if (tbl_rev != 2) {
0668 ret = PTR_ERR(wifi_pkg);
0669 goto out_free;
0670 }
0671
0672 num_chains = ACPI_SAR_NUM_CHAINS_REV2;
0673 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;
0674
0675 goto read_table;
0676 }
0677
0678
0679 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
0680 ACPI_EWRD_WIFI_DATA_SIZE_REV1,
0681 &tbl_rev);
0682 if (!IS_ERR(wifi_pkg)) {
0683 if (tbl_rev != 1) {
0684 ret = PTR_ERR(wifi_pkg);
0685 goto out_free;
0686 }
0687
0688 num_chains = ACPI_SAR_NUM_CHAINS_REV1;
0689 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;
0690
0691 goto read_table;
0692 }
0693
0694
0695 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
0696 ACPI_EWRD_WIFI_DATA_SIZE_REV0,
0697 &tbl_rev);
0698 if (!IS_ERR(wifi_pkg)) {
0699 if (tbl_rev != 0) {
0700 ret = PTR_ERR(wifi_pkg);
0701 goto out_free;
0702 }
0703
0704 num_chains = ACPI_SAR_NUM_CHAINS_REV0;
0705 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;
0706
0707 goto read_table;
0708 }
0709
0710 ret = PTR_ERR(wifi_pkg);
0711 goto out_free;
0712
0713 read_table:
0714 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
0715 wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER) {
0716 ret = -EINVAL;
0717 goto out_free;
0718 }
0719
0720 enabled = !!(wifi_pkg->package.elements[1].integer.value);
0721 n_profiles = wifi_pkg->package.elements[2].integer.value;
0722
0723
0724
0725
0726
0727
0728 if (n_profiles <= 0 || n_profiles >= ACPI_SAR_PROFILE_NUM) {
0729 ret = -EINVAL;
0730 goto out_free;
0731 }
0732
0733
0734 pos = 3;
0735
0736 for (i = 0; i < n_profiles; i++) {
0737
0738
0739
0740
0741 ret = iwl_sar_set_profile(&wifi_pkg->package.elements[pos],
0742 &fwrt->sar_profiles[i + 1], enabled,
0743 num_chains, num_sub_bands);
0744 if (ret < 0)
0745 break;
0746
0747
0748 pos += num_chains * num_sub_bands;
0749 }
0750
0751 out_free:
0752 kfree(data);
0753 return ret;
0754 }
0755 IWL_EXPORT_SYMBOL(iwl_sar_get_ewrd_table);
0756
0757 int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt)
0758 {
0759 union acpi_object *wifi_pkg, *data;
0760 int i, j, k, ret, tbl_rev;
0761 u8 num_bands, num_profiles;
0762 static const struct {
0763 u8 revisions;
0764 u8 bands;
0765 u8 profiles;
0766 u8 min_profiles;
0767 } rev_data[] = {
0768 {
0769 .revisions = BIT(3),
0770 .bands = ACPI_GEO_NUM_BANDS_REV2,
0771 .profiles = ACPI_NUM_GEO_PROFILES_REV3,
0772 .min_profiles = 3,
0773 },
0774 {
0775 .revisions = BIT(2),
0776 .bands = ACPI_GEO_NUM_BANDS_REV2,
0777 .profiles = ACPI_NUM_GEO_PROFILES,
0778 },
0779 {
0780 .revisions = BIT(0) | BIT(1),
0781 .bands = ACPI_GEO_NUM_BANDS_REV0,
0782 .profiles = ACPI_NUM_GEO_PROFILES,
0783 },
0784 };
0785 int idx;
0786
0787 int entry_idx = 1;
0788
0789 BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES_REV3 != IWL_NUM_GEO_PROFILES_V3);
0790 BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES != IWL_NUM_GEO_PROFILES);
0791
0792 data = iwl_acpi_get_object(fwrt->dev, ACPI_WGDS_METHOD);
0793 if (IS_ERR(data))
0794 return PTR_ERR(data);
0795
0796
0797 for (idx = 0; idx < ARRAY_SIZE(rev_data); idx++) {
0798
0799 u32 hdr_size = 1 + !!rev_data[idx].min_profiles;
0800 u32 profile_size = ACPI_GEO_PER_CHAIN_SIZE *
0801 rev_data[idx].bands;
0802 u32 max_size = hdr_size + profile_size * rev_data[idx].profiles;
0803 u32 min_size;
0804
0805 if (!rev_data[idx].min_profiles)
0806 min_size = max_size;
0807 else
0808 min_size = hdr_size +
0809 profile_size * rev_data[idx].min_profiles;
0810
0811 wifi_pkg = iwl_acpi_get_wifi_pkg_range(fwrt->dev, data,
0812 min_size, max_size,
0813 &tbl_rev);
0814 if (!IS_ERR(wifi_pkg)) {
0815 if (!(BIT(tbl_rev) & rev_data[idx].revisions))
0816 continue;
0817
0818 num_bands = rev_data[idx].bands;
0819 num_profiles = rev_data[idx].profiles;
0820
0821 if (rev_data[idx].min_profiles) {
0822
0823 union acpi_object *entry;
0824
0825 entry = &wifi_pkg->package.elements[entry_idx];
0826 entry_idx++;
0827 if (entry->type != ACPI_TYPE_INTEGER ||
0828 entry->integer.value > num_profiles) {
0829 ret = -EINVAL;
0830 goto out_free;
0831 }
0832 num_profiles = entry->integer.value;
0833
0834
0835
0836
0837
0838
0839 if (wifi_pkg->package.count !=
0840 hdr_size + profile_size * num_profiles) {
0841 ret = -EINVAL;
0842 goto out_free;
0843 }
0844 }
0845 goto read_table;
0846 }
0847 }
0848
0849 if (idx < ARRAY_SIZE(rev_data))
0850 ret = PTR_ERR(wifi_pkg);
0851 else
0852 ret = -ENOENT;
0853 goto out_free;
0854
0855 read_table:
0856 fwrt->geo_rev = tbl_rev;
0857 for (i = 0; i < num_profiles; i++) {
0858 for (j = 0; j < ACPI_GEO_NUM_BANDS_REV2; j++) {
0859 union acpi_object *entry;
0860
0861
0862
0863
0864
0865
0866 if (j >= num_bands) {
0867 fwrt->geo_profiles[i].bands[j].max =
0868 fwrt->geo_profiles[i].bands[1].max;
0869 } else {
0870 entry = &wifi_pkg->package.elements[entry_idx];
0871 entry_idx++;
0872 if (entry->type != ACPI_TYPE_INTEGER ||
0873 entry->integer.value > U8_MAX) {
0874 ret = -EINVAL;
0875 goto out_free;
0876 }
0877
0878 fwrt->geo_profiles[i].bands[j].max =
0879 entry->integer.value;
0880 }
0881
0882 for (k = 0; k < ACPI_GEO_NUM_CHAINS; k++) {
0883
0884 if (j >= num_bands) {
0885 fwrt->geo_profiles[i].bands[j].chains[k] =
0886 fwrt->geo_profiles[i].bands[1].chains[k];
0887 } else {
0888 entry = &wifi_pkg->package.elements[entry_idx];
0889 entry_idx++;
0890 if (entry->type != ACPI_TYPE_INTEGER ||
0891 entry->integer.value > U8_MAX) {
0892 ret = -EINVAL;
0893 goto out_free;
0894 }
0895
0896 fwrt->geo_profiles[i].bands[j].chains[k] =
0897 entry->integer.value;
0898 }
0899 }
0900 }
0901 }
0902
0903 fwrt->geo_num_profiles = num_profiles;
0904 fwrt->geo_enabled = true;
0905 ret = 0;
0906 out_free:
0907 kfree(data);
0908 return ret;
0909 }
0910 IWL_EXPORT_SYMBOL(iwl_sar_get_wgds_table);
0911
0912 bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt)
0913 {
0914
0915
0916
0917
0918
0919
0920
0921
0922
0923
0924
0925 return IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) >= 38 ||
0926 (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 17 &&
0927 fwrt->trans->hw_rev != CSR_HW_REV_TYPE_3160) ||
0928 (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 29 &&
0929 ((fwrt->trans->hw_rev & CSR_HW_REV_TYPE_MSK) ==
0930 CSR_HW_REV_TYPE_7265D));
0931 }
0932 IWL_EXPORT_SYMBOL(iwl_sar_geo_support);
0933
0934 int iwl_sar_geo_init(struct iwl_fw_runtime *fwrt,
0935 struct iwl_per_chain_offset *table,
0936 u32 n_bands, u32 n_profiles)
0937 {
0938 int i, j;
0939
0940 if (!fwrt->geo_enabled)
0941 return -ENODATA;
0942
0943 if (!iwl_sar_geo_support(fwrt))
0944 return -EOPNOTSUPP;
0945
0946 for (i = 0; i < n_profiles; i++) {
0947 for (j = 0; j < n_bands; j++) {
0948 struct iwl_per_chain_offset *chain =
0949 &table[i * n_bands + j];
0950
0951 chain->max_tx_power =
0952 cpu_to_le16(fwrt->geo_profiles[i].bands[j].max);
0953 chain->chain_a = fwrt->geo_profiles[i].bands[j].chains[0];
0954 chain->chain_b = fwrt->geo_profiles[i].bands[j].chains[1];
0955 IWL_DEBUG_RADIO(fwrt,
0956 "SAR geographic profile[%d] Band[%d]: chain A = %d chain B = %d max_tx_power = %d\n",
0957 i, j,
0958 fwrt->geo_profiles[i].bands[j].chains[0],
0959 fwrt->geo_profiles[i].bands[j].chains[1],
0960 fwrt->geo_profiles[i].bands[j].max);
0961 }
0962 }
0963
0964 return 0;
0965 }
0966 IWL_EXPORT_SYMBOL(iwl_sar_geo_init);
0967
0968 __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt)
0969 {
0970 int ret;
0971 u8 value;
0972 __le32 config_bitmap = 0;
0973
0974
0975
0976
0977 ret = iwl_acpi_get_dsm_u8(fwrt->dev, 0,
0978 DSM_FUNC_ENABLE_INDONESIA_5G2,
0979 &iwl_guid, &value);
0980
0981 if (!ret && value == DSM_VALUE_INDONESIA_ENABLE)
0982 config_bitmap |=
0983 cpu_to_le32(LARI_CONFIG_ENABLE_5G2_IN_INDONESIA_MSK);
0984
0985
0986
0987
0988 ret = iwl_acpi_get_dsm_u8(fwrt->dev, 0,
0989 DSM_FUNC_DISABLE_SRD,
0990 &iwl_guid, &value);
0991 if (!ret) {
0992 if (value == DSM_VALUE_SRD_PASSIVE)
0993 config_bitmap |=
0994 cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_PASSIVE_MSK);
0995 else if (value == DSM_VALUE_SRD_DISABLE)
0996 config_bitmap |=
0997 cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_DISABLED_MSK);
0998 }
0999
1000 return config_bitmap;
1001 }
1002 IWL_EXPORT_SYMBOL(iwl_acpi_get_lari_config_bitmap);
1003
1004 int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt)
1005 {
1006 union acpi_object *wifi_pkg, *data, *flags;
1007 int i, j, ret, tbl_rev, num_sub_bands = 0;
1008 int idx = 2;
1009
1010 fwrt->ppag_flags = 0;
1011
1012 data = iwl_acpi_get_object(fwrt->dev, ACPI_PPAG_METHOD);
1013 if (IS_ERR(data))
1014 return PTR_ERR(data);
1015
1016
1017 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
1018 ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev);
1019
1020 if (!IS_ERR(wifi_pkg)) {
1021 if (tbl_rev == 1 || tbl_rev == 2) {
1022 num_sub_bands = IWL_NUM_SUB_BANDS_V2;
1023 IWL_DEBUG_RADIO(fwrt,
1024 "Reading PPAG table v2 (tbl_rev=%d)\n",
1025 tbl_rev);
1026 goto read_table;
1027 } else {
1028 ret = -EINVAL;
1029 goto out_free;
1030 }
1031 }
1032
1033
1034 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
1035 ACPI_PPAG_WIFI_DATA_SIZE_V1, &tbl_rev);
1036
1037 if (!IS_ERR(wifi_pkg)) {
1038 if (tbl_rev != 0) {
1039 ret = -EINVAL;
1040 goto out_free;
1041 }
1042 num_sub_bands = IWL_NUM_SUB_BANDS_V1;
1043 IWL_DEBUG_RADIO(fwrt, "Reading PPAG table v1 (tbl_rev=0)\n");
1044 goto read_table;
1045 }
1046
1047 read_table:
1048 fwrt->ppag_ver = tbl_rev;
1049 flags = &wifi_pkg->package.elements[1];
1050
1051 if (flags->type != ACPI_TYPE_INTEGER) {
1052 ret = -EINVAL;
1053 goto out_free;
1054 }
1055
1056 fwrt->ppag_flags = flags->integer.value & ACPI_PPAG_MASK;
1057
1058 if (!fwrt->ppag_flags) {
1059 ret = 0;
1060 goto out_free;
1061 }
1062
1063
1064
1065
1066
1067
1068 for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
1069 for (j = 0; j < num_sub_bands; j++) {
1070 union acpi_object *ent;
1071
1072 ent = &wifi_pkg->package.elements[idx++];
1073 if (ent->type != ACPI_TYPE_INTEGER) {
1074 ret = -EINVAL;
1075 goto out_free;
1076 }
1077
1078 fwrt->ppag_chains[i].subbands[j] = ent->integer.value;
1079
1080 if ((j == 0 &&
1081 (fwrt->ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_LB ||
1082 fwrt->ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_LB)) ||
1083 (j != 0 &&
1084 (fwrt->ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_HB ||
1085 fwrt->ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_HB))) {
1086 fwrt->ppag_flags = 0;
1087 ret = -EINVAL;
1088 goto out_free;
1089 }
1090 }
1091 }
1092
1093
1094 ret = 0;
1095
1096 out_free:
1097 kfree(data);
1098 return ret;
1099 }
1100 IWL_EXPORT_SYMBOL(iwl_acpi_get_ppag_table);
1101
1102 int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *cmd,
1103 int *cmd_size)
1104 {
1105 u8 cmd_ver;
1106 int i, j, num_sub_bands;
1107 s8 *gain;
1108
1109 if (!fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) {
1110 IWL_DEBUG_RADIO(fwrt,
1111 "PPAG capability not supported by FW, command not sent.\n");
1112 return -EINVAL;
1113 }
1114 if (!fwrt->ppag_flags) {
1115 IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n");
1116 return -EINVAL;
1117 }
1118
1119
1120
1121
1122 cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags);
1123 cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw,
1124 WIDE_ID(PHY_OPS_GROUP, PER_PLATFORM_ANT_GAIN_CMD),
1125 IWL_FW_CMD_VER_UNKNOWN);
1126 if (cmd_ver == 1) {
1127 num_sub_bands = IWL_NUM_SUB_BANDS_V1;
1128 gain = cmd->v1.gain[0];
1129 *cmd_size = sizeof(cmd->v1);
1130 if (fwrt->ppag_ver == 1 || fwrt->ppag_ver == 2) {
1131 IWL_DEBUG_RADIO(fwrt,
1132 "PPAG table rev is %d but FW supports v1, sending truncated table\n",
1133 fwrt->ppag_ver);
1134 cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK);
1135 }
1136 } else if (cmd_ver == 2 || cmd_ver == 3) {
1137 num_sub_bands = IWL_NUM_SUB_BANDS_V2;
1138 gain = cmd->v2.gain[0];
1139 *cmd_size = sizeof(cmd->v2);
1140 if (fwrt->ppag_ver == 0) {
1141 IWL_DEBUG_RADIO(fwrt,
1142 "PPAG table is v1 but FW supports v2, sending padded table\n");
1143 } else if (cmd_ver == 2 && fwrt->ppag_ver == 2) {
1144 IWL_DEBUG_RADIO(fwrt,
1145 "PPAG table is v3 but FW supports v2, sending partial bitmap.\n");
1146 cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK);
1147 }
1148 } else {
1149 IWL_DEBUG_RADIO(fwrt, "Unsupported PPAG command version\n");
1150 return -EINVAL;
1151 }
1152
1153 for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
1154 for (j = 0; j < num_sub_bands; j++) {
1155 gain[i * num_sub_bands + j] =
1156 fwrt->ppag_chains[i].subbands[j];
1157 IWL_DEBUG_RADIO(fwrt,
1158 "PPAG table: chain[%d] band[%d]: gain = %d\n",
1159 i, j, gain[i * num_sub_bands + j]);
1160 }
1161 }
1162
1163 return 0;
1164 }
1165 IWL_EXPORT_SYMBOL(iwl_read_ppag_table);
1166
1167 bool iwl_acpi_is_ppag_approved(struct iwl_fw_runtime *fwrt)
1168 {
1169
1170 if (!dmi_check_system(dmi_ppag_approved_list)) {
1171 IWL_DEBUG_RADIO(fwrt,
1172 "System vendor '%s' is not in the approved list, disabling PPAG.\n",
1173 dmi_get_system_info(DMI_SYS_VENDOR));
1174 fwrt->ppag_flags = 0;
1175 return false;
1176 }
1177
1178 return true;
1179 }
1180 IWL_EXPORT_SYMBOL(iwl_acpi_is_ppag_approved);