iwlwifi: mvm: add RFI-M support
RF Interference Mitigation is a new feature targeted to handle the problem of interference between DDR memory and WiFi. The role of the driver is to configure FW with the table holding a mapping between problematic channels/bands and the corresponding frequencies. This patch adds RFI infrastructure and adds two debugfs hooks: - send RFI configuration command (currently with a default table) which will reset feature in the FW - read the table, used by the FW (which can be a subset of the table that driver sent). Signed-off-by: Gregory Greenman <gregory.greenman@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com> Link: https://lore.kernel.org/r/iwlwifi.20210210171218.2cea55a09bc7.I634b79795abad499ce442631d6672ffef8fc6d41@changeid Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
This commit is contained in:
Родитель
3ce882473e
Коммит
21254908cb
|
@ -606,6 +606,16 @@ enum iwl_system_subcmd_ids {
|
|||
* @FW_ERROR_RECOVERY_CMD: &struct iwl_fw_error_recovery_cmd
|
||||
*/
|
||||
FW_ERROR_RECOVERY_CMD = 0x7,
|
||||
|
||||
/**
|
||||
* @RFI_CONFIG_CMD: &struct iwl_rfi_config_cmd
|
||||
*/
|
||||
RFI_CONFIG_CMD = 0xb,
|
||||
|
||||
/**
|
||||
* @RFI_GET_FREQ_TABLE_CMD: &struct iwl_rfi_config_cmd
|
||||
*/
|
||||
RFI_GET_FREQ_TABLE_CMD = 0xc,
|
||||
};
|
||||
|
||||
#endif /* __iwl_fw_api_commands_h__ */
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2020 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_fw_api_rfi_h__
|
||||
#define __iwl_fw_api_rfi_h__
|
||||
|
||||
#define IWL_RFI_LUT_ENTRY_CHANNELS_NUM 15
|
||||
#define IWL_RFI_LUT_SIZE 24
|
||||
#define IWL_RFI_LUT_INSTALLED_SIZE 4
|
||||
|
||||
/**
|
||||
* struct iwl_rfi_lut_entry - an entry in the RFI frequency LUT.
|
||||
*
|
||||
* @freq: frequency
|
||||
* @channels: channels that can be interfered at frequency freq (at most 15)
|
||||
* @bands: the corresponding bands
|
||||
*/
|
||||
struct iwl_rfi_lut_entry {
|
||||
__le16 freq;
|
||||
u8 channels[IWL_RFI_LUT_ENTRY_CHANNELS_NUM];
|
||||
u8 bands[IWL_RFI_LUT_ENTRY_CHANNELS_NUM];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct iwl_rfi_config_cmd - RFI configuration table
|
||||
*
|
||||
* @entry: a table can have 24 frequency/channel mappings
|
||||
* @oem: specifies if this is the default table or set by OEM
|
||||
*/
|
||||
struct iwl_rfi_config_cmd {
|
||||
struct iwl_rfi_lut_entry table[IWL_RFI_LUT_SIZE];
|
||||
u8 oem;
|
||||
u8 reserved[3];
|
||||
} __packed; /* RFI_CONFIG_CMD_API_S_VER_1 */
|
||||
|
||||
/**
|
||||
* iwl_rfi_freq_table_status - status of the frequency table query
|
||||
* @RFI_FREQ_TABLE_OK: can be used
|
||||
* @RFI_FREQ_TABLE_DVFS_NOT_READY: DVFS is not ready yet, should try later
|
||||
* @RFI_FREQ_TABLE_DISABLED: the feature is disabled in FW
|
||||
*/
|
||||
enum iwl_rfi_freq_table_status {
|
||||
RFI_FREQ_TABLE_OK,
|
||||
RFI_FREQ_TABLE_DVFS_NOT_READY,
|
||||
RFI_FREQ_TABLE_DISABLED,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_rfi_freq_table_resp_cmd - get the rfi freq table used by FW
|
||||
*
|
||||
* @table: table used by FW
|
||||
* @status: see &iwl_rfi_freq_table_status
|
||||
*/
|
||||
struct iwl_rfi_freq_table_resp_cmd {
|
||||
struct iwl_rfi_lut_entry table[IWL_RFI_LUT_INSTALLED_SIZE];
|
||||
__le32 status;
|
||||
} __packed; /* RFI_CONFIG_CMD_API_S_VER_1 */
|
||||
|
||||
#endif /* __iwl_fw_api_rfi_h__ */
|
|
@ -441,6 +441,7 @@ enum iwl_ucode_tlv_capa {
|
|||
IWL_UCODE_TLV_CAPA_PSC_CHAN_SUPPORT = (__force iwl_ucode_tlv_capa_t)98,
|
||||
|
||||
IWL_UCODE_TLV_CAPA_BIGTK_SUPPORT = (__force iwl_ucode_tlv_capa_t)100,
|
||||
IWL_UCODE_TLV_CAPA_RFIM_SUPPORT = (__force iwl_ucode_tlv_capa_t)102,
|
||||
|
||||
NUM_IWL_UCODE_TLV_CAPA
|
||||
#ifdef __CHECKER__
|
||||
|
|
|
@ -6,6 +6,7 @@ iwlmvm-y += scan.o time-event.o rs.o rs-fw.o
|
|||
iwlmvm-y += power.o coex.o
|
||||
iwlmvm-y += tt.o offloading.o tdls.o
|
||||
iwlmvm-y += ftm-responder.o ftm-initiator.o
|
||||
iwlmvm-y += rfi.o
|
||||
iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
|
||||
iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o
|
||||
iwlmvm-$(CONFIG_PM) += d3.o
|
||||
|
|
|
@ -1776,6 +1776,69 @@ iwl_dbgfs_ltr_config_write(struct iwl_mvm *mvm,
|
|||
return ret ?: count;
|
||||
}
|
||||
|
||||
static ssize_t iwl_dbgfs_rfi_freq_table_write(struct iwl_mvm *mvm, char *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int ret = 0;
|
||||
u16 op_id;
|
||||
|
||||
if (kstrtou16(buf, 10, &op_id))
|
||||
return -EINVAL;
|
||||
|
||||
/* value zero triggers re-sending the default table to the device */
|
||||
if (!op_id)
|
||||
ret = iwl_rfi_send_config_cmd(mvm, NULL);
|
||||
else
|
||||
ret = -EOPNOTSUPP; /* in the future a new table will be added */
|
||||
|
||||
return ret ?: count;
|
||||
}
|
||||
|
||||
/* The size computation is as follows:
|
||||
* each number needs at most 3 characters, number of rows is the size of
|
||||
* the table; So, need 5 chars for the "freq: " part and each tuple afterwards
|
||||
* needs 6 characters for numbers and 5 for the punctuation around.
|
||||
*/
|
||||
#define IWL_RFI_BUF_SIZE (IWL_RFI_LUT_INSTALLED_SIZE *\
|
||||
(5 + IWL_RFI_LUT_ENTRY_CHANNELS_NUM * (6 + 5)))
|
||||
|
||||
static ssize_t iwl_dbgfs_rfi_freq_table_read(struct file *file,
|
||||
char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct iwl_mvm *mvm = file->private_data;
|
||||
struct iwl_rfi_freq_table_resp_cmd *resp;
|
||||
u32 status;
|
||||
char buf[IWL_RFI_BUF_SIZE];
|
||||
int i, j, pos = 0;
|
||||
|
||||
resp = iwl_rfi_get_freq_table(mvm);
|
||||
if (IS_ERR(resp))
|
||||
return PTR_ERR(resp);
|
||||
|
||||
status = le32_to_cpu(resp->status);
|
||||
if (status != RFI_FREQ_TABLE_OK) {
|
||||
scnprintf(buf, IWL_RFI_BUF_SIZE, "status = %d\n", status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(resp->table); i++) {
|
||||
pos += scnprintf(buf + pos, IWL_RFI_BUF_SIZE - pos, "%d: ",
|
||||
resp->table[i].freq);
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(resp->table[i].channels); j++)
|
||||
pos += scnprintf(buf + pos, IWL_RFI_BUF_SIZE - pos,
|
||||
"(%d, %d) ",
|
||||
resp->table[i].channels[j],
|
||||
resp->table[i].bands[j]);
|
||||
pos += scnprintf(buf + pos, IWL_RFI_BUF_SIZE - pos, "\n");
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(resp);
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
|
||||
}
|
||||
|
||||
MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64);
|
||||
|
||||
/* Device wide debugfs entries */
|
||||
|
@ -1827,6 +1890,7 @@ MVM_DEBUGFS_READ_WRITE_STA_FILE_OPS(amsdu_len, 16);
|
|||
MVM_DEBUGFS_READ_WRITE_FILE_OPS(he_sniffer_params, 32);
|
||||
|
||||
MVM_DEBUGFS_WRITE_FILE_OPS(ltr_config, 512);
|
||||
MVM_DEBUGFS_READ_WRITE_FILE_OPS(rfi_freq_table, 16);
|
||||
|
||||
static ssize_t iwl_dbgfs_mem_read(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
|
@ -2010,6 +2074,7 @@ void iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
|
|||
MVM_DEBUGFS_ADD_FILE(inject_packet, mvm->debugfs_dir, 0200);
|
||||
MVM_DEBUGFS_ADD_FILE(inject_beacon_ie, mvm->debugfs_dir, 0200);
|
||||
MVM_DEBUGFS_ADD_FILE(inject_beacon_ie_restore, mvm->debugfs_dir, 0200);
|
||||
MVM_DEBUGFS_ADD_FILE(rfi_freq_table, mvm->debugfs_dir, 0600);
|
||||
|
||||
if (mvm->fw->phy_integration_ver)
|
||||
MVM_DEBUGFS_ADD_FILE(phy_integration_ver, mvm->debugfs_dir, 0400);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2012-2014, 2018 Intel Corporation
|
||||
* Copyright (C) 2012-2014, 2018, 2020 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
@ -36,5 +36,6 @@
|
|||
#include "fw/api/stats.h"
|
||||
#include "fw/api/location.h"
|
||||
#include "fw/api/tx.h"
|
||||
#include "fw/api/rfi.h"
|
||||
|
||||
#endif /* __fw_api_h__ */
|
||||
|
|
|
@ -2037,6 +2037,10 @@ void iwl_mvm_sta_add_debugfs(struct ieee80211_hw *hw,
|
|||
struct dentry *dir);
|
||||
#endif
|
||||
|
||||
int iwl_rfi_send_config_cmd(struct iwl_mvm *mvm,
|
||||
struct iwl_rfi_lut_entry *rfi_table);
|
||||
struct iwl_rfi_freq_table_resp_cmd *iwl_rfi_get_freq_table(struct iwl_mvm *mvm);
|
||||
|
||||
static inline u8 iwl_mvm_phy_band_from_nl80211(enum nl80211_band band)
|
||||
{
|
||||
switch (band) {
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2020 Intel Corporation
|
||||
*/
|
||||
|
||||
#include "mvm.h"
|
||||
#include "fw/api/commands.h"
|
||||
#include "fw/api/phy-ctxt.h"
|
||||
|
||||
/**
|
||||
* DDR needs frequency in units of 16.666MHz, so provide FW with the
|
||||
* frequency values in the adjusted format.
|
||||
*/
|
||||
const static struct iwl_rfi_lut_entry iwl_rfi_table[IWL_RFI_LUT_SIZE] = {
|
||||
/* LPDDR4 */
|
||||
|
||||
/* frequency 3733MHz */
|
||||
{cpu_to_le16(223), {114, 116, 118, 120, 122,},
|
||||
{PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5,}},
|
||||
|
||||
/* frequency 4267MHz */
|
||||
{cpu_to_le16(256), {79, 83, 85, 87, 89, 91, 93,},
|
||||
{PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,
|
||||
PHY_BAND_6, PHY_BAND_6,}},
|
||||
|
||||
/* DDR5ePOR */
|
||||
|
||||
/* frequency 4000MHz */
|
||||
{cpu_to_le16(240), {3, 5, 7, 9, 11, 13, 15,},
|
||||
{PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,
|
||||
PHY_BAND_6, PHY_BAND_6,}},
|
||||
|
||||
/* frequency 4400MHz */
|
||||
{cpu_to_le16(264), {111, 119, 123, 125, 129, 131, 133, 135, 143,},
|
||||
{PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,
|
||||
PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,}},
|
||||
|
||||
/* LPDDR5iPOR */
|
||||
|
||||
/* frequency 5200MHz */
|
||||
{cpu_to_le16(312), {36, 38, 40, 42, 50,},
|
||||
{PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5,}},
|
||||
|
||||
/* frequency 6000MHz */
|
||||
{cpu_to_le16(360), {3, 5, 7, 9, 11, 13, 15,},
|
||||
{PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,
|
||||
PHY_BAND_6, PHY_BAND_6,}},
|
||||
|
||||
/* frequency 6400MHz */
|
||||
{cpu_to_le16(384), {79, 83, 85, 87, 89, 91, 93,},
|
||||
{PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,
|
||||
PHY_BAND_6, PHY_BAND_6,}},
|
||||
};
|
||||
|
||||
int iwl_rfi_send_config_cmd(struct iwl_mvm *mvm, struct iwl_rfi_lut_entry *rfi_table)
|
||||
{
|
||||
int ret;
|
||||
struct iwl_rfi_config_cmd cmd;
|
||||
struct iwl_host_cmd hcmd = {
|
||||
.id = WIDE_ID(SYSTEM_GROUP, RFI_CONFIG_CMD),
|
||||
.dataflags[0] = IWL_HCMD_DFL_DUP,
|
||||
.data[0] = &cmd,
|
||||
.len[0] = sizeof(cmd),
|
||||
};
|
||||
|
||||
if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_RFIM_SUPPORT))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* in case no table is passed, use the default one */
|
||||
if (!rfi_table) {
|
||||
memcpy(cmd.table, iwl_rfi_table, sizeof(cmd.table));
|
||||
} else {
|
||||
memcpy(cmd.table, rfi_table, sizeof(cmd.table));
|
||||
/* notify FW the table is not the default one */
|
||||
cmd.oem = 1;
|
||||
}
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
ret = iwl_mvm_send_cmd(mvm, &hcmd);
|
||||
mutex_unlock(&mvm->mutex);
|
||||
|
||||
if (ret)
|
||||
IWL_ERR(mvm, "Failed to send RFI config cmd %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct iwl_rfi_freq_table_resp_cmd *iwl_rfi_get_freq_table(struct iwl_mvm *mvm)
|
||||
{
|
||||
struct iwl_rfi_freq_table_resp_cmd *resp;
|
||||
int resp_size = sizeof(*resp);
|
||||
int ret;
|
||||
struct iwl_host_cmd cmd = {
|
||||
.id = WIDE_ID(SYSTEM_GROUP, RFI_GET_FREQ_TABLE_CMD),
|
||||
.flags = CMD_WANT_SKB,
|
||||
};
|
||||
|
||||
if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_RFIM_SUPPORT))
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
ret = iwl_mvm_send_cmd(mvm, &cmd);
|
||||
mutex_unlock(&mvm->mutex);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (WARN_ON_ONCE(iwl_rx_packet_payload_len(cmd.resp_pkt) != resp_size))
|
||||
return ERR_PTR(-EIO);
|
||||
|
||||
resp = kzalloc(resp_size, GFP_KERNEL);
|
||||
if (!resp)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memcpy(resp, cmd.resp_pkt->data, resp_size);
|
||||
|
||||
iwl_free_resp(&cmd);
|
||||
return resp;
|
||||
}
|
Загрузка…
Ссылка в новой задаче