blob: 7fdb7986effc5235ead0df298f9401203b3a2cb0 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2020 - 2024 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.
*/
static const
struct iwl_rfi_ddr_lut_entry iwl_rfi_ddr_table[IWL_RFI_DDR_LUT_SIZE] = {
/* frequency 2600MHz */
{cpu_to_le16(156), {34, 36, 38, 40, 42, 50},
{PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5,
PHY_BAND_5,}},
/* frequency 2667MHz */
{cpu_to_le16(160), {50, 58, 60, 62, 64, 68},
{PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5,
PHY_BAND_5,}},
/* frequency 2800MHz */
{cpu_to_le16(168), {114, 116, 118, 120, 122},
{PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5,}},
/* frequency 2933MHz */
{cpu_to_le16(176), {163, 167, 169, 171, 173, 175},
{PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5,
PHY_BAND_5,}},
/* frequency 3000MHz */
{cpu_to_le16(180), {3, 5, 7, 9, 11, 15, 31},
{PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,
PHY_BAND_6, PHY_BAND_6,}},
/* frequency 3067MHz */
{cpu_to_le16(184), {15, 23, 27, 29, 31, 33, 35, 37, 39, 47, 63},
{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, PHY_BAND_6,
PHY_BAND_6,}},
/* frequency 3200MHz */
{cpu_to_le16(192), {63, 79, 83, 85, 87, 89, 91, 95},
{PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,
PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,}},
/* frequency 3300MHz */
{cpu_to_le16(198), {95, 111, 119, 123, 125, 129, 127, 131, 135, 143,
159},
{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, PHY_BAND_6,
PHY_BAND_6,}},
/* frequency 3400MHz */
{cpu_to_le16(204), {159, 163, 165, 167, 169, 171, 175, 191},
{PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,
PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,}},
/* frequency 3733MHz */
{cpu_to_le16(224), {114, 116, 118, 120, 122},
{PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5,}},
/* frequency 4000MHz */
{cpu_to_le16(240), {3, 5, 7, 9, 11, 15, 31},
{PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,
PHY_BAND_6, PHY_BAND_6,}},
/* frequency 4200MHz */
{cpu_to_le16(252), {63, 65, 67, 69, 71, 79, 95},
{PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,
PHY_BAND_6, PHY_BAND_6,}},
/* frequency 4267MHz */
{cpu_to_le16(256), {63, 79, 83, 85, 87, 89, 91, 95},
{PHY_BAND_6, 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), {95, 111, 119, 123, 125, 127, 129, 131, 135, 143,
159},
{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, PHY_BAND_6,
PHY_BAND_6,}},
/* frequency 4600MHz */
{cpu_to_le16(276), {159, 175, 183, 185, 187, 189, 191},
{PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,
PHY_BAND_6, PHY_BAND_6,}},
/* frequency 4800MHz */
{cpu_to_le16(288), {1, 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, PHY_BAND_6,}},
/* frequency 5200MHz */
{cpu_to_le16(312), {34, 36, 38, 40, 42, 50},
{PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5,
PHY_BAND_5,}},
/* frequency 5333MHz */
{cpu_to_le16(320), {50, 58, 60, 62, 64, 68},
{PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5,
PHY_BAND_5,}},
/* frequency 5600MHz */
{cpu_to_le16(336), {114, 116, 118, 120, 122},
{PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5, PHY_BAND_5,}},
/* frequency 5868MHz */
{cpu_to_le16(352), {163, 167, 169, 171, 173, 175},
{PHY_BAND_5, 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, 15, 31},
{PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,
PHY_BAND_6, PHY_BAND_6,}},
/* frequency 6133MHz */
{cpu_to_le16(368), {15, 23, 27, 29, 31, 33, 35, 37, 39, 47, 63},
{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, PHY_BAND_6,
PHY_BAND_6,}},
/* frequency 6400MHz */
{cpu_to_le16(384), {63, 79, 83, 85, 87, 89, 91, 95,},
{PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,
PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,}},
/* frequency 6600MHz */
{cpu_to_le16(396), {95, 111, 119, 123, 125, 127, 129, 131, 135, 143,
159},
{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, PHY_BAND_6,
PHY_BAND_6,}},
/* frequency 6800MHz */
{cpu_to_le16(408), {159, 163, 165, 167, 169, 171, 175, 191},
{PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,
PHY_BAND_6, PHY_BAND_6, PHY_BAND_6,}},
/* frequency 6933MHz */
{cpu_to_le16(416), {159, 175, 183, 187, 189, 191, 193, 195, 197, 199,
207},
{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, PHY_BAND_6,
PHY_BAND_6,}},
};
static inline bool iwl_rfi_enabled_by_mac_type(struct iwl_mvm *mvm,
bool so_rfi_mode)
{
u32 mac_type = CSR_HW_REV_TYPE(mvm->trans->hw_rev);
bool enable_rfi = false;
if ((mac_type != IWL_CFG_ANY && mac_type >= IWL_CFG_MAC_TYPE_MA) ||
(mac_type == IWL_CFG_MAC_TYPE_SO && so_rfi_mode))
enable_rfi = true;
return enable_rfi;
}
bool iwl_rfi_supported(struct iwl_mvm *mvm, bool so_rfi_mode, bool is_ddr)
{
bool rfi_enable_mac_type = iwl_rfi_enabled_by_mac_type(mvm,
so_rfi_mode);
bool ddr_capa = fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_RFI_DDR_SUPPORT);
bool dlvr_capa = fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_RFI_DLVR_SUPPORT);
IWL_DEBUG_FW(mvm, "FW has RFI DDR capability:%s DLVR capability:%s\n",
ddr_capa ? "yes" : "no", dlvr_capa ? "yes" : "no");
IWL_DEBUG_FW(mvm,
"HW is integrated:%s rfi_enabled:%s fw_rfi_state:%d\n",
mvm->trans->trans_cfg->integrated ? "yes" : "no",
rfi_enable_mac_type ? "yes" : "no", mvm->fw_rfi_state);
return (is_ddr ? ddr_capa : dlvr_capa) && mvm->bios_enable_rfi &&
rfi_enable_mac_type && mvm->trans->trans_cfg->integrated &&
iwl_mvm_fw_rfi_state_supported(mvm);
}
int iwl_rfi_send_config_cmd(struct iwl_mvm *mvm,
struct iwl_rfi_ddr_lut_entry *rfi_ddr_table,
bool is_set_master_cmd, bool force_send_table)
{
struct iwl_rfi_config_cmd *cmd = NULL;
bool rfi_ddr_support;
bool rfi_dlvr_support;
bool old_force_rfi = mvm->force_enable_rfi;
bool so_rfi_mode;
int ret = 0;
struct iwl_host_cmd hcmd = {
.id = WIDE_ID(SYSTEM_GROUP, RFI_CONFIG_CMD),
.dataflags[0] = IWL_HCMD_DFL_DUP,
.len[0] = sizeof(*cmd),
};
u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
WIDE_ID(SYSTEM_GROUP,
RFI_CONFIG_CMD), 0);
if (cmd_ver != 3)
return -EOPNOTSUPP;
/* for SO, rfi support is enabled only when vendor
* command explicitly asked us to manage RFI.
* vendor command can change SO enablement mode
*/
if (is_set_master_cmd) {
mvm->force_enable_rfi = mvm->rfi_wlan_master;
so_rfi_mode = old_force_rfi ? true : mvm->force_enable_rfi;
} else {
so_rfi_mode = mvm->force_enable_rfi;
}
rfi_ddr_support = iwl_rfi_supported(mvm, so_rfi_mode, true);
rfi_dlvr_support = iwl_rfi_supported(mvm, so_rfi_mode, false);
if (!rfi_ddr_support && !rfi_dlvr_support)
return -EOPNOTSUPP;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
hcmd.data[0] = cmd;
lockdep_assert_held(&mvm->mutex);
IWL_DEBUG_FW(mvm, "wlan is %s rfi master\n",
mvm->rfi_wlan_master ? "" : "Not");
/* Drop stored rfi_config_cmd buffer when there is change in master */
if (is_set_master_cmd) {
kfree(mvm->iwl_prev_rfi_config_cmd);
mvm->iwl_prev_rfi_config_cmd = NULL;
}
/* Zero iwl_rfi_config_cmd is legal for FW API and since it has
* rfi_memory_support equal to 0, it will disable all RFIm operation
* in the FW.
* Not having rfi_ddr_table when wlan driver is not the master means
* user-space requested to stop RFIm.
*/
if (!rfi_ddr_table && !mvm->rfi_wlan_master) {
if (force_send_table || !mvm->iwl_prev_rfi_config_cmd ||
memcmp(mvm->iwl_prev_rfi_config_cmd, cmd, sizeof(*cmd))) {
IWL_DEBUG_FW(mvm, "Sending zero DDR superset table\n");
goto send_empty_cmd;
} else {
IWL_DEBUG_FW(mvm, "Skip RFI_CONFIG_CMD sending\n");
goto out;
}
}
if (rfi_ddr_support) {
cmd->rfi_memory_support = cpu_to_le32(RFI_DDR_SUPPORTED_MSK);
/* don't send RFI_CONFIG_CMD to FW when DDR table passed by
* caller and previously sent table is same.
*/
if (!force_send_table && rfi_ddr_table &&
mvm->iwl_prev_rfi_config_cmd &&
!memcmp(rfi_ddr_table,
mvm->iwl_prev_rfi_config_cmd->ddr_table,
sizeof(mvm->iwl_prev_rfi_config_cmd->ddr_table))) {
IWL_DEBUG_FW(mvm, "Skip RFI_CONFIG_CMD sending\n");
goto out;
/* send RFI_CONFIG_CMD to FW with OEM ddr table */
} else if (rfi_ddr_table) {
IWL_DEBUG_FW(mvm, "Sending oem DDR superset table\n");
memcpy(cmd->ddr_table, rfi_ddr_table,
sizeof(cmd->ddr_table));
/* notify FW the table is not the default one */
cmd->oem = 1;
/* send previous RFI_CONFIG_CMD once again as FW lost RFI DDR
* table in reset
*/
} else if (mvm->iwl_prev_rfi_config_cmd && force_send_table) {
IWL_DEBUG_FW(mvm,
"Sending buffered %s DDR superset table\n",
mvm->iwl_prev_rfi_config_cmd->oem ?
"oem" : "default");
memcpy(cmd->ddr_table,
mvm->iwl_prev_rfi_config_cmd->ddr_table,
sizeof(cmd->ddr_table));
cmd->oem = mvm->iwl_prev_rfi_config_cmd->oem;
/* don't send previous RFI_CONFIG_CMD as FW has same table */
} else if (mvm->iwl_prev_rfi_config_cmd) {
IWL_DEBUG_FW(mvm, "Skip RFI_CONFIG_CMD sending\n");
goto out;
/* send default ddr table for the first time */
} else {
IWL_DEBUG_FW(mvm,
"Sending default DDR superset table\n");
memcpy(cmd->ddr_table, iwl_rfi_ddr_table,
sizeof(cmd->ddr_table));
}
}
if (rfi_dlvr_support)
cmd->rfi_memory_support |= cpu_to_le32(RFI_DLVR_SUPPORTED_MSK);
send_empty_cmd:
ret = iwl_mvm_send_cmd(mvm, &hcmd);
kfree(mvm->iwl_prev_rfi_config_cmd);
if (ret) {
mvm->iwl_prev_rfi_config_cmd = NULL;
IWL_ERR(mvm, "Failed to send RFI config cmd %d\n", ret);
} else {
mvm->iwl_prev_rfi_config_cmd = cmd;
cmd = NULL;
}
out:
kfree(cmd);
return ret;
}
void *iwl_rfi_get_freq_table(struct iwl_mvm *mvm)
{
void *resp;
int resp_size;
int ret;
struct iwl_host_cmd cmd = {
.id = WIDE_ID(SYSTEM_GROUP, RFI_GET_FREQ_TABLE_CMD),
.flags = CMD_WANT_SKB,
};
u8 notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, SYSTEM_GROUP,
RFI_GET_FREQ_TABLE_CMD,
IWL_FW_CMD_VER_UNKNOWN);
if (notif_ver == 1)
resp_size = sizeof(struct iwl_rfi_freq_table_resp_cmd_v1);
else if (notif_ver == 2)
resp_size = sizeof(struct iwl_rfi_freq_table_resp_cmd);
else
return ERR_PTR(-EOPNOTSUPP);
if (!iwl_rfi_supported(mvm, mvm->force_enable_rfi, true))
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)) {
iwl_free_resp(&cmd);
return ERR_PTR(-EIO);
}
resp = kmemdup(cmd.resp_pkt->data, resp_size, GFP_KERNEL);
iwl_free_resp(&cmd);
if (!resp)
return ERR_PTR(-ENOMEM);
return resp;
}
void iwl_rfi_support_notif_handler(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_rfi_support_notif *notif = (void *)pkt->data;
mvm->fw_rfi_state = le32_to_cpu(notif->reason);
switch (mvm->fw_rfi_state) {
case IWL_RFI_DDR_SUBSET_TABLE_READY:
IWL_DEBUG_FW(mvm, "RFIm, DDR subset table ready\n");
break;
case IWL_RFI_PMC_SUPPORTED:
IWL_DEBUG_FW(mvm, "RFIm, PMC supported\n");
break;
case IWL_RFI_PMC_NOT_SUPPORTED:
IWL_DEBUG_FW(mvm, "RFIm, PMC not supported\n");
break;
case IWL_RFI_RESET_FAILURE_SEND_TO_PEER:
fallthrough;
case IWL_RFI_RESET_FAILURE_PLAT_PSS:
fallthrough;
case IWL_RFI_RESET_FAILURE_TIMER:
fallthrough;
case IWL_RFI_MAX_RESETS_DONE:
fallthrough;
default:
IWL_DEBUG_FW(mvm, "RFIm is deactivated, reason = %d\n",
mvm->fw_rfi_state);
}
}