Commit 21254908 authored by Gregory Greenman's avatar Gregory Greenman Committed by Luca Coelho

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: default avatarGregory Greenman <gregory.greenman@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
Link: https://lore.kernel.org/r/iwlwifi.20210210171218.2cea55a09bc7.I634b79795abad499ce442631d6672ffef8fc6d41@changeidSigned-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent 3ce88247
......@@ -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__ */
/* 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);
......
/* 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) {
......
// 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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment