|
@@ -65,6 +65,7 @@
|
|
|
*****************************************************************************/
|
|
|
#include <net/mac80211.h>
|
|
|
#include <linux/netdevice.h>
|
|
|
+#include <linux/acpi.h>
|
|
|
|
|
|
#include "iwl-trans.h"
|
|
|
#include "iwl-op-mode.h"
|
|
@@ -902,6 +903,174 @@ static int iwl_mvm_config_ltr(struct iwl_mvm *mvm)
|
|
|
sizeof(cmd), &cmd);
|
|
|
}
|
|
|
|
|
|
+#define ACPI_WRDS_METHOD "WRDS"
|
|
|
+#define ACPI_WRDS_WIFI (0x07)
|
|
|
+#define ACPI_WRDS_TABLE_SIZE 10
|
|
|
+
|
|
|
+struct iwl_mvm_sar_table {
|
|
|
+ bool enabled;
|
|
|
+ u8 values[ACPI_WRDS_TABLE_SIZE];
|
|
|
+};
|
|
|
+
|
|
|
+#ifdef CONFIG_ACPI
|
|
|
+static int iwl_mvm_sar_get_wrds(struct iwl_mvm *mvm, union acpi_object *wrds,
|
|
|
+ struct iwl_mvm_sar_table *sar_table)
|
|
|
+{
|
|
|
+ union acpi_object *data_pkg;
|
|
|
+ u32 i;
|
|
|
+
|
|
|
+ /* We need at least two packages, one for the revision and one
|
|
|
+ * for the data itself. Also check that the revision is valid
|
|
|
+ * (i.e. it is an integer set to 0).
|
|
|
+ */
|
|
|
+ if (wrds->type != ACPI_TYPE_PACKAGE ||
|
|
|
+ wrds->package.count < 2 ||
|
|
|
+ wrds->package.elements[0].type != ACPI_TYPE_INTEGER ||
|
|
|
+ wrds->package.elements[0].integer.value != 0) {
|
|
|
+ IWL_DEBUG_RADIO(mvm, "Unsupported wrds structure\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* loop through all the packages to find the one for WiFi */
|
|
|
+ for (i = 1; i < wrds->package.count; i++) {
|
|
|
+ union acpi_object *domain;
|
|
|
+
|
|
|
+ data_pkg = &wrds->package.elements[i];
|
|
|
+
|
|
|
+ /* Skip anything that is not a package with the right
|
|
|
+ * amount of elements (i.e. domain_type,
|
|
|
+ * enabled/disabled plus the sar table size.
|
|
|
+ */
|
|
|
+ if (data_pkg->type != ACPI_TYPE_PACKAGE ||
|
|
|
+ data_pkg->package.count != ACPI_WRDS_TABLE_SIZE + 2)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ domain = &data_pkg->package.elements[0];
|
|
|
+ if (domain->type == ACPI_TYPE_INTEGER &&
|
|
|
+ domain->integer.value == ACPI_WRDS_WIFI)
|
|
|
+ break;
|
|
|
+
|
|
|
+ data_pkg = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!data_pkg)
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ if (data_pkg->package.elements[1].type != ACPI_TYPE_INTEGER)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ sar_table->enabled = !!(data_pkg->package.elements[1].integer.value);
|
|
|
+
|
|
|
+ for (i = 0; i < ACPI_WRDS_TABLE_SIZE; i++) {
|
|
|
+ union acpi_object *entry;
|
|
|
+
|
|
|
+ entry = &data_pkg->package.elements[i + 2];
|
|
|
+ if ((entry->type != ACPI_TYPE_INTEGER) ||
|
|
|
+ (entry->integer.value > U8_MAX))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ sar_table->values[i] = entry->integer.value;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int iwl_mvm_sar_get_table(struct iwl_mvm *mvm,
|
|
|
+ struct iwl_mvm_sar_table *sar_table)
|
|
|
+{
|
|
|
+ acpi_handle root_handle;
|
|
|
+ acpi_handle handle;
|
|
|
+ struct acpi_buffer wrds = {ACPI_ALLOCATE_BUFFER, NULL};
|
|
|
+ acpi_status status;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ root_handle = ACPI_HANDLE(mvm->dev);
|
|
|
+ if (!root_handle) {
|
|
|
+ IWL_DEBUG_RADIO(mvm,
|
|
|
+ "Could not retrieve root port ACPI handle\n");
|
|
|
+ return -ENOENT;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Get the method's handle */
|
|
|
+ status = acpi_get_handle(root_handle, (acpi_string)ACPI_WRDS_METHOD,
|
|
|
+ &handle);
|
|
|
+ if (ACPI_FAILURE(status)) {
|
|
|
+ IWL_DEBUG_RADIO(mvm, "WRDS method not found\n");
|
|
|
+ return -ENOENT;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Call WRDS with no arguments */
|
|
|
+ status = acpi_evaluate_object(handle, NULL, NULL, &wrds);
|
|
|
+ if (ACPI_FAILURE(status)) {
|
|
|
+ IWL_DEBUG_RADIO(mvm, "WRDS invocation failed (0x%x)\n", status);
|
|
|
+ return -ENOENT;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = iwl_mvm_sar_get_wrds(mvm, wrds.pointer, sar_table);
|
|
|
+ kfree(wrds.pointer);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+#else /* CONFIG_ACPI */
|
|
|
+static int iwl_mvm_sar_get_table(struct iwl_mvm *mvm,
|
|
|
+ struct iwl_mvm_sar_table *sar_table)
|
|
|
+{
|
|
|
+ return -ENOENT;
|
|
|
+}
|
|
|
+#endif /* CONFIG_ACPI */
|
|
|
+
|
|
|
+static int iwl_mvm_sar_init(struct iwl_mvm *mvm)
|
|
|
+{
|
|
|
+ struct iwl_mvm_sar_table sar_table;
|
|
|
+ struct iwl_dev_tx_power_cmd cmd = {
|
|
|
+ .v2.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS),
|
|
|
+ };
|
|
|
+ int ret, i, j, idx;
|
|
|
+
|
|
|
+ /* we can't do anything with the table if the FW doesn't support it */
|
|
|
+ if (!fw_has_api(&mvm->fw->ucode_capa,
|
|
|
+ IWL_UCODE_TLV_API_TX_POWER_CHAIN)) {
|
|
|
+ IWL_DEBUG_RADIO(mvm,
|
|
|
+ "FW doesn't support per-chain TX power settings.\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = iwl_mvm_sar_get_table(mvm, &sar_table);
|
|
|
+ if (ret < 0) {
|
|
|
+ IWL_DEBUG_RADIO(mvm,
|
|
|
+ "SAR BIOS table invalid or unavailable. (%d)\n",
|
|
|
+ ret);
|
|
|
+ /* we don't fail if the table is not available */
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!sar_table.enabled)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ IWL_DEBUG_RADIO(mvm, "Sending REDUCE_TX_POWER_CMD per chain\n");
|
|
|
+
|
|
|
+ BUILD_BUG_ON(IWL_NUM_CHAIN_LIMITS * IWL_NUM_SUB_BANDS !=
|
|
|
+ ACPI_WRDS_TABLE_SIZE);
|
|
|
+
|
|
|
+ for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
|
|
|
+ IWL_DEBUG_RADIO(mvm, " Chain[%d]:\n", i);
|
|
|
+ for (j = 0; j < IWL_NUM_SUB_BANDS; j++) {
|
|
|
+ idx = (i * IWL_NUM_SUB_BANDS) + j;
|
|
|
+ cmd.per_chain_restriction[i][j] =
|
|
|
+ cpu_to_le16(sar_table.values[idx]);
|
|
|
+ IWL_DEBUG_RADIO(mvm, " Band[%d] = %d * .125dBm\n",
|
|
|
+ j, sar_table.values[idx]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0,
|
|
|
+ sizeof(cmd), &cmd);
|
|
|
+ if (ret)
|
|
|
+ IWL_ERR(mvm, "failed to set per-chain TX power: %d\n", ret);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
int iwl_mvm_up(struct iwl_mvm *mvm)
|
|
|
{
|
|
|
int ret, i;
|
|
@@ -1077,6 +1246,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
|
|
|
if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
|
|
|
iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
|
|
|
|
|
|
+ ret = iwl_mvm_sar_init(mvm);
|
|
|
+ if (ret)
|
|
|
+ goto error;
|
|
|
+
|
|
|
IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
|
|
|
return 0;
|
|
|
error:
|