iwlwifi: support per-platform antenna gain
authorGil Adam <gil.adam@intel.com>
Sun, 16 Jun 2019 10:18:28 +0000 (13:18 +0300)
committerLuca Coelho <luciano.coelho@intel.com>
Fri, 6 Sep 2019 12:31:20 +0000 (15:31 +0300)
TX power limits as defined in the OTP assume the worst case scenario
in terms of the platform's atenna gain, but most platforms are below
that value so they can use more TX power without passing the regulatory
limit. If the platform indicates in the BIOS that it indeed has lower
gain, and the geographic location allows it, higher TX power can be
used. The driver reads the PPAG (Per-Platform Antenna Gain) data from
BIOS (if it exists), validates it and sends the appropriate command to
the FW. This flow happens once at FW init, in case of suspend/resume
there is no need to read again from BIOS as we save those values during
init, so just send the PPAG command again to FW.

Signed-off-by: Gil Adam <gil.adam@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
drivers/net/wireless/intel/iwlwifi/fw/acpi.h
drivers/net/wireless/intel/iwlwifi/fw/api/phy.h
drivers/net/wireless/intel/iwlwifi/fw/api/power.h
drivers/net/wireless/intel/iwlwifi/fw/file.h
drivers/net/wireless/intel/iwlwifi/mvm/d3.c
drivers/net/wireless/intel/iwlwifi/mvm/fw.c
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h

index 991a234509994952774582307b316351ef53b13f..6cb2d1f5efea6d6cf30b3054eda368c1af1c6fdd 100644 (file)
@@ -68,6 +68,7 @@
 #define ACPI_WRDD_METHOD       "WRDD"
 #define ACPI_SPLC_METHOD       "SPLC"
 #define ACPI_ECKV_METHOD       "ECKV"
+#define ACPI_PPAG_METHOD       "PPAG"
 
 #define ACPI_WIFI_DOMAIN       (0x07)
 
 #define ACPI_WGDS_NUM_BANDS            2
 #define ACPI_WGDS_TABLE_SIZE           3
 
+#define ACPI_PPAG_NUM_CHAINS           2
+#define ACPI_PPAG_NUM_SUB_BANDS                5
+#define ACPI_PPAG_WIFI_DATA_SIZE       ((ACPI_PPAG_NUM_CHAINS * \
+                                       ACPI_PPAG_NUM_SUB_BANDS) + 3)
+
+/* PPAG gain value bounds in 1/8 dBm */
+#define ACPI_PPAG_MIN_LB -16
+#define ACPI_PPAG_MAX_LB 24
+#define ACPI_PPAG_MIN_HB -16
+#define ACPI_PPAG_MAX_HB 40
+
 #ifdef CONFIG_ACPI
 
 void *iwl_acpi_get_object(struct device *dev, acpi_string method);
index 9cc59e00bd95f2afaf209a7d4466617a09729e5d..8991ddffbf5e18452388676424eeceab1f30ea35 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2019 Intel Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,6 +31,7 @@
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright(c) 2019 Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -89,6 +91,11 @@ enum iwl_phy_ops_subcmd_ids {
         */
        GEO_TX_POWER_LIMIT = 0x05,
 
+       /**
+        * @PER_PLATFORM_ANT_GAIN_CMD: &struct iwl_ppag_table_cmd
+        */
+       PER_PLATFORM_ANT_GAIN_CMD = 0x07,
+
        /**
         * @CT_KILL_NOTIFICATION: &struct ct_kill_notif
         */
index f195db398bedb87eeb70c86e2d3bc734a6c8c80f..6e1b9b21904e93c8e3153ced3ede22385bef5d0d 100644 (file)
@@ -449,6 +449,18 @@ struct iwl_geo_tx_power_profiles_resp {
        __le32 profile_idx;
 } __packed; /* GEO_TX_POWER_LIMIT_RESP */
 
+/**
+ * struct iwl_ppag_table_cmd - struct for PER_PLATFORM_ANT_GAIN_CMD cmd.
+ * @enabled: 1 if PPAG is enabled, 0 otherwise
+ * @gain: table of antenna gain values per chain and sub-band
+ * @reserved: reserved
+ */
+struct iwl_ppag_table_cmd {
+       __le32 enabled;
+       s8 gain[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS];
+       s8 reserved[2];
+} __packed; /* PER_PLATFORM_ANT_GAIN_CMD */
+
 /**
  * struct iwl_beacon_filter_cmd
  * REPLY_BEACON_FILTERING_CMD = 0xd2 (command)
index 3d567240ef11d8a280f5d53b46f85309bdb02139..73cd41480c2fb239c06c3e373e800407e323f8d8 100644 (file)
@@ -446,6 +446,7 @@ enum iwl_ucode_tlv_capa {
        IWL_UCODE_TLV_CAPA_ULTRA_HB_CHANNELS            = (__force iwl_ucode_tlv_capa_t)48,
        IWL_UCODE_TLV_CAPA_CS_MODIFY                    = (__force iwl_ucode_tlv_capa_t)49,
        IWL_UCODE_TLV_CAPA_SET_LTR_GEN2                 = (__force iwl_ucode_tlv_capa_t)50,
+       IWL_UCODE_TLV_CAPA_SET_PPAG                     = (__force iwl_ucode_tlv_capa_t)52,
 
        /* set 2 */
        IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE         = (__force iwl_ucode_tlv_capa_t)64,
index 6f7345b121a614b74d9bce9a64ef4b314d45ada2..2bf5b83e116cb463b45a258964e418512f199c00 100644 (file)
@@ -1965,6 +1965,9 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
         */
        iwl_mvm_update_changed_regdom(mvm);
 
+       /* Re-configure PPAG settings */
+       iwl_mvm_ppag_send_cmd(mvm);
+
        if (!unified_image)
                /*  Re-configure default SAR profile */
                iwl_mvm_sar_select_profile(mvm, 1, 1);
index 0f6925a514351f71756bedf8a3bef462e88a6b28..af46723f790fb99e48238171c420566d933d3791 100644 (file)
@@ -1002,6 +1002,113 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm)
        return iwl_mvm_send_cmd_pdu(mvm, cmd_wide_id, 0, sizeof(cmd), &cmd);
 }
 
+static int iwl_mvm_get_ppag_table(struct iwl_mvm *mvm)
+{
+       union acpi_object *wifi_pkg, *data, *enabled;
+       int i, j, ret, tbl_rev;
+       int idx = 2;
+
+       mvm->ppag_table.enabled = cpu_to_le32(0);
+       data = iwl_acpi_get_object(mvm->dev, ACPI_PPAG_METHOD);
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data,
+                                        ACPI_PPAG_WIFI_DATA_SIZE, &tbl_rev);
+
+       if (IS_ERR(wifi_pkg) || tbl_rev != 0) {
+               ret = PTR_ERR(wifi_pkg);
+               goto out_free;
+       }
+
+       enabled = &wifi_pkg->package.elements[1];
+       if (enabled->type != ACPI_TYPE_INTEGER ||
+           (enabled->integer.value != 0 && enabled->integer.value != 1)) {
+               ret = -EINVAL;
+               goto out_free;
+       }
+
+       mvm->ppag_table.enabled = cpu_to_le32(enabled->integer.value);
+       if (!mvm->ppag_table.enabled) {
+               ret = 0;
+               goto out_free;
+       }
+
+       /*
+        * read, verify gain values and save them into the PPAG table.
+        * first sub-band (j=0) corresponds to Low-Band (2.4GHz), and the
+        * following sub-bands to High-Band (5GHz).
+        */
+       for (i = 0; i < ACPI_PPAG_NUM_CHAINS; i++) {
+               for (j = 0; j < ACPI_PPAG_NUM_SUB_BANDS; j++) {
+                       union acpi_object *ent;
+
+                       ent = &wifi_pkg->package.elements[idx++];
+                       if (ent->type != ACPI_TYPE_INTEGER ||
+                           (j == 0 && ent->integer.value > ACPI_PPAG_MAX_LB) ||
+                           (j == 0 && ent->integer.value < ACPI_PPAG_MIN_LB) ||
+                           (j != 0 && ent->integer.value > ACPI_PPAG_MAX_HB) ||
+                           (j != 0 && ent->integer.value < ACPI_PPAG_MIN_HB)) {
+                               mvm->ppag_table.enabled = cpu_to_le32(0);
+                               ret = -EINVAL;
+                               goto out_free;
+                       }
+                       mvm->ppag_table.gain[i][j] = ent->integer.value;
+               }
+       }
+       ret = 0;
+out_free:
+       kfree(data);
+       return ret;
+}
+
+int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm)
+{
+       int i, j, ret;
+
+       if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) {
+               IWL_DEBUG_RADIO(mvm,
+                               "PPAG capability not supported by FW, command not sent.\n");
+               return 0;
+       }
+
+       IWL_DEBUG_RADIO(mvm, "Sending PER_PLATFORM_ANT_GAIN_CMD\n");
+       IWL_DEBUG_RADIO(mvm, "PPAG is %s\n",
+                       mvm->ppag_table.enabled ? "enabled" : "disabled");
+
+       for (i = 0; i < ACPI_PPAG_NUM_CHAINS; i++) {
+               for (j = 0; j < ACPI_PPAG_NUM_SUB_BANDS; j++) {
+                       IWL_DEBUG_RADIO(mvm,
+                                       "PPAG table: chain[%d] band[%d]: gain = %d\n",
+                                       i, j, mvm->ppag_table.gain[i][j]);
+               }
+       }
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP,
+                                               PER_PLATFORM_ANT_GAIN_CMD),
+                                  0, sizeof(mvm->ppag_table),
+                                  &mvm->ppag_table);
+       if (ret < 0)
+               IWL_ERR(mvm, "failed to send PER_PLATFORM_ANT_GAIN_CMD (%d)\n",
+                       ret);
+
+       return ret;
+}
+
+static int iwl_mvm_ppag_init(struct iwl_mvm *mvm)
+{
+       int ret;
+
+       ret = iwl_mvm_get_ppag_table(mvm);
+       if (ret < 0) {
+               IWL_DEBUG_RADIO(mvm,
+                               "PPAG BIOS table invalid or unavailable. (%d)\n",
+                               ret);
+               return 0;
+       }
+       return iwl_mvm_ppag_send_cmd(mvm);
+}
+
 #else /* CONFIG_ACPI */
 static int iwl_mvm_sar_get_wrds_table(struct iwl_mvm *mvm)
 {
@@ -1033,6 +1140,16 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm)
 {
        return -ENOENT;
 }
+
+int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm)
+{
+       return -ENOENT;
+}
+
+static int iwl_mvm_ppag_init(struct iwl_mvm *mvm)
+{
+       return -ENOENT;
+}
 #endif /* CONFIG_ACPI */
 
 void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags)
@@ -1325,6 +1442,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        if (iwl_acpi_get_eckv(mvm->dev, &mvm->ext_clock_valid))
                IWL_DEBUG_INFO(mvm, "ECKV table doesn't exist in BIOS\n");
 
+       ret = iwl_mvm_ppag_init(mvm);
+       if (ret)
+               goto error;
+
        ret = iwl_mvm_sar_init(mvm);
        if (ret == 0) {
                ret = iwl_mvm_sar_geo_init(mvm);
index 79bbdf8121cc26e8186089ca925a4504c30fcb04..0ea0f72880afab171b3deae3cdb613355516a4a2 100644 (file)
@@ -1136,6 +1136,8 @@ struct iwl_mvm {
        struct iwl_mvm_sar_profile sar_profiles[ACPI_SAR_PROFILE_NUM];
        struct iwl_mvm_geo_profile geo_profiles[ACPI_NUM_GEO_PROFILES];
        u32 geo_rev;
+       struct iwl_ppag_table_cmd ppag_table;
+       u32 ppag_rev;
 #endif
 };
 
@@ -2050,6 +2052,7 @@ void iwl_mvm_event_frame_timeout_callback(struct iwl_mvm *mvm,
 
 int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b);
 int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm);
+int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 void iwl_mvm_sta_add_debugfs(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,