mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-07-05 13:25:20 +02:00
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: Evan Quan <quanliangl@hotmail.com> Reviewed-by: Mario Limonciello <mario.limonciello@amd.com> Signed-off-by: Ma Jun <Jun.Ma2@amd.com> Signed-off-by: Ma Jun <Jun.Ma2@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
296b29ce8a
commit
b8b39de646
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user