Commit b8b39de6 authored by Evan Quan's avatar Evan Quan Committed by Alex Deucher

drm/amd/pm: setup the framework to support Wifi RFI mitigation feature

With WBRF feature supported, as a driver responding to the frequencies,
amdgpu driver is able to do shadow pstate switching to mitigate possible
interference(between its (G-)DDR memory clocks and local radio module
frequency bands used by Wifi 6/6e/7).

--
v1->v2:
  - update the prompt for feature support(Lijo)
v8->v9:
  - update parameter document for smu_wbrf_event_handler(Simon)
v9->v10:
v10->v11:
 - correct the logics for wbrf range sorting(Lijo)
v13:
 - Fix the format issue (IIpo Jarvinen)
Signed-off-by: default avatarEvan Quan <quanliangl@hotmail.com>
Reviewed-by: default avatarMario Limonciello <mario.limonciello@amd.com>
Signed-off-by: default avatarMa Jun <Jun.Ma2@amd.com>
Signed-off-by: default avatarMa Jun <Jun.Ma2@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 296b29ce
......@@ -252,6 +252,8 @@ extern int amdgpu_seamless;
extern int amdgpu_user_partt_mode;
extern int amdgpu_agp;
extern int amdgpu_wbrf;
#define AMDGPU_VM_MAX_NUM_CTX 4096
#define AMDGPU_SG_THRESHOLD (256*1024*1024)
#define AMDGPU_WAIT_IDLE_TIMEOUT_IN_MS 3000
......
......@@ -208,6 +208,7 @@ int amdgpu_umsch_mm;
int amdgpu_seamless = -1; /* auto */
uint amdgpu_debug_mask;
int amdgpu_agp = -1; /* auto */
int amdgpu_wbrf = -1;
static void amdgpu_drv_delayed_reset_work_handler(struct work_struct *work);
......@@ -971,6 +972,22 @@ module_param_named(debug_mask, amdgpu_debug_mask, uint, 0444);
MODULE_PARM_DESC(agp, "AGP (-1 = auto (default), 0 = disable, 1 = enable)");
module_param_named(agp, amdgpu_agp, int, 0444);
/**
* DOC: wbrf (int)
* Enable Wifi RFI interference mitigation feature.
* Due to electrical and mechanical constraints there may be likely interference of
* relatively high-powered harmonics of the (G-)DDR memory clocks with local radio
* module frequency bands used by Wifi 6/6e/7. To mitigate the possible RFI interference,
* with this feature enabled, PMFW will use either “shadowed P-State” or “P-State” based
* on active list of frequencies in-use (to be avoided) as part of initial setting or
* P-state transition. However, there may be potential performance impact with this
* feature enabled.
* (0 = disabled, 1 = enabled, -1 = auto (default setting, will be enabled if supported))
*/
MODULE_PARM_DESC(wbrf,
"Enable Wifi RFI interference mitigation (0 = disabled, 1 = enabled, -1 = auto(default)");
module_param_named(wbrf, amdgpu_wbrf, int, 0444);
/* These devices are not supported by amdgpu.
* They are supported by the mach64, r128, radeon drivers
*/
......
......@@ -1322,6 +1322,170 @@ static int smu_get_thermal_temperature_range(struct smu_context *smu)
return ret;
}
/**
* smu_wbrf_handle_exclusion_ranges - consume the wbrf exclusion ranges
*
* @smu: smu_context pointer
*
* Retrieve the wbrf exclusion ranges and send them to PMFW for proper handling.
* Returns 0 on success, error on failure.
*/
static int smu_wbrf_handle_exclusion_ranges(struct smu_context *smu)
{
struct wbrf_ranges_in_out wbrf_exclusion = {0};
struct freq_band_range *wifi_bands = wbrf_exclusion.band_list;
struct amdgpu_device *adev = smu->adev;
uint32_t num_of_wbrf_ranges = MAX_NUM_OF_WBRF_RANGES;
uint64_t start, end;
int ret, i, j;
ret = amd_wbrf_retrieve_freq_band(adev->dev, &wbrf_exclusion);
if (ret) {
dev_err(adev->dev, "Failed to retrieve exclusion ranges!\n");
return ret;
}
/*
* The exclusion ranges array we got might be filled with holes and duplicate
* entries. For example:
* {(2400, 2500), (0, 0), (6882, 6962), (2400, 2500), (0, 0), (6117, 6189), (0, 0)...}
* We need to do some sortups to eliminate those holes and duplicate entries.
* Expected output: {(2400, 2500), (6117, 6189), (6882, 6962), (0, 0)...}
*/
for (i = 0; i < num_of_wbrf_ranges; i++) {
start = wifi_bands[i].start;
end = wifi_bands[i].end;
/* get the last valid entry to fill the intermediate hole */
if (!start && !end) {
for (j = num_of_wbrf_ranges - 1; j > i; j--)
if (wifi_bands[j].start && wifi_bands[j].end)
break;
/* no valid entry left */
if (j <= i)
break;
start = wifi_bands[i].start = wifi_bands[j].start;
end = wifi_bands[i].end = wifi_bands[j].end;
wifi_bands[j].start = 0;
wifi_bands[j].end = 0;
num_of_wbrf_ranges = j;
}
/* eliminate duplicate entries */
for (j = i + 1; j < num_of_wbrf_ranges; j++) {
if ((wifi_bands[j].start == start) && (wifi_bands[j].end == end)) {
wifi_bands[j].start = 0;
wifi_bands[j].end = 0;
}
}
}
/* Send the sorted wifi_bands to PMFW */
ret = smu_set_wbrf_exclusion_ranges(smu, wifi_bands);
/* Try to set the wifi_bands again */
if (unlikely(ret == -EBUSY)) {
mdelay(5);
ret = smu_set_wbrf_exclusion_ranges(smu, wifi_bands);
}
return ret;
}
/**
* smu_wbrf_event_handler - handle notify events
*
* @nb: notifier block
* @action: event type
* @_arg: event data
*
* Calls relevant amdgpu function in response to wbrf event
* notification from kernel.
*/
static int smu_wbrf_event_handler(struct notifier_block *nb,
unsigned long action, void *_arg)
{
struct smu_context *smu = container_of(nb, struct smu_context, wbrf_notifier);
switch (action) {
case WBRF_CHANGED:
smu_wbrf_handle_exclusion_ranges(smu);
break;
default:
return NOTIFY_DONE;
};
return NOTIFY_OK;
}
/**
* smu_wbrf_support_check - check wbrf support
*
* @smu: smu_context pointer
*
* Verifies the ACPI interface whether wbrf is supported.
*/
static void smu_wbrf_support_check(struct smu_context *smu)
{
struct amdgpu_device *adev = smu->adev;
smu->wbrf_supported = smu_is_asic_wbrf_supported(smu) && amdgpu_wbrf &&
acpi_amd_wbrf_supported_consumer(adev->dev);
if (smu->wbrf_supported)
dev_info(adev->dev, "RF interference mitigation is supported\n");
}
/**
* smu_wbrf_init - init driver wbrf support
*
* @smu: smu_context pointer
*
* Verifies the AMD ACPI interfaces and registers with the wbrf
* notifier chain if wbrf feature is supported.
* Returns 0 on success, error on failure.
*/
static int smu_wbrf_init(struct smu_context *smu)
{
struct amdgpu_device *adev = smu->adev;
int ret;
if (!smu->wbrf_supported)
return 0;
smu->wbrf_notifier.notifier_call = smu_wbrf_event_handler;
ret = amd_wbrf_register_notifier(&smu->wbrf_notifier);
if (ret)
return ret;
/*
* Some wifiband exclusion ranges may be already there
* before our driver loaded. To make sure our driver
* is awared of those exclusion ranges.
*/
ret = smu_wbrf_handle_exclusion_ranges(smu);
if (ret)
dev_err(adev->dev, "Failed to handle wbrf exclusion ranges\n");
return ret;
}
/**
* smu_wbrf_fini - tear down driver wbrf support
*
* @smu: smu_context pointer
*
* Unregisters with the wbrf notifier chain.
*/
static void smu_wbrf_fini(struct smu_context *smu)
{
if (!smu->wbrf_supported)
return;
amd_wbrf_unregister_notifier(&smu->wbrf_notifier);
}
static int smu_smc_hw_setup(struct smu_context *smu)
{
struct smu_feature *feature = &smu->smu_feature;
......@@ -1414,6 +1578,15 @@ static int smu_smc_hw_setup(struct smu_context *smu)
if (ret)
return ret;
/* Enable UclkShadow on wbrf supported */
if (smu->wbrf_supported) {
ret = smu_enable_uclk_shadow(smu, true);
if (ret) {
dev_err(adev->dev, "Failed to enable UclkShadow feature to support wbrf!\n");
return ret;
}
}
/*
* With SCPM enabled, these actions(and relevant messages) are
* not needed and permitted.
......@@ -1512,6 +1685,15 @@ static int smu_smc_hw_setup(struct smu_context *smu)
*/
ret = smu_set_min_dcef_deep_sleep(smu,
smu->smu_table.boot_values.dcefclk / 100);
if (ret) {
dev_err(adev->dev, "Error setting min deepsleep dcefclk\n");
return ret;
}
/* Init wbrf support. Properly setup the notifier */
ret = smu_wbrf_init(smu);
if (ret)
dev_err(adev->dev, "Error during wbrf init call\n");
return ret;
}
......@@ -1567,6 +1749,13 @@ static int smu_hw_init(void *handle)
return ret;
}
/*
* Check whether wbrf is supported. This needs to be done
* before SMU setup starts since part of SMU configuration
* relies on this.
*/
smu_wbrf_support_check(smu);
if (smu->is_apu) {
ret = smu_set_gfx_imu_enable(smu);
if (ret)
......@@ -1733,6 +1922,8 @@ static int smu_smc_hw_cleanup(struct smu_context *smu)
struct amdgpu_device *adev = smu->adev;
int ret = 0;
smu_wbrf_fini(smu);
cancel_work_sync(&smu->throttling_logging_work);
cancel_work_sync(&smu->interrupt_work);
......
......@@ -22,6 +22,8 @@
#ifndef __AMDGPU_SMU_H__
#define __AMDGPU_SMU_H__
#include <linux/acpi_amd_wbrf.h>
#include "amdgpu.h"
#include "kgd_pp_interface.h"
#include "dm_pp_interface.h"
......@@ -570,6 +572,10 @@ struct smu_context {
struct delayed_work swctf_delayed_work;
enum pp_xgmi_plpd_mode plpd_mode;
/* data structures for wbrf feature support */
bool wbrf_supported;
struct notifier_block wbrf_notifier;
};
struct i2c_adapter;
......@@ -1375,6 +1381,22 @@ struct pptable_funcs {
* @notify_rlc_state: Notify RLC power state to SMU.
*/
int (*notify_rlc_state)(struct smu_context *smu, bool en);
/**
* @is_asic_wbrf_supported: check whether PMFW supports the wbrf feature
*/
bool (*is_asic_wbrf_supported)(struct smu_context *smu);
/**
* @enable_uclk_shadow: Enable the uclk shadow feature on wbrf supported
*/
int (*enable_uclk_shadow)(struct smu_context *smu, bool enable);
/**
* @set_wbrf_exclusion_ranges: notify SMU the wifi bands occupied
*/
int (*set_wbrf_exclusion_ranges)(struct smu_context *smu,
struct freq_band_range *exclusion_ranges);
};
typedef enum {
......
......@@ -98,6 +98,9 @@
#define smu_set_config_table(smu, config_table) smu_ppt_funcs(set_config_table, -EOPNOTSUPP, smu, config_table)
#define smu_init_pptable_microcode(smu) smu_ppt_funcs(init_pptable_microcode, 0, smu)
#define smu_notify_rlc_state(smu, en) smu_ppt_funcs(notify_rlc_state, 0, smu, en)
#define smu_is_asic_wbrf_supported(smu) smu_ppt_funcs(is_asic_wbrf_supported, false, smu)
#define smu_enable_uclk_shadow(smu, enable) smu_ppt_funcs(enable_uclk_shadow, 0, smu, enable)
#define smu_set_wbrf_exclusion_ranges(smu, freq_band_range) smu_ppt_funcs(set_wbrf_exclusion_ranges, -EOPNOTSUPP, smu, freq_band_range)
#endif
#endif
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