From: Evan Quan <hidden> Date: 2023-08-18 03:27:50
Due to electrical and mechanical constraints in certain platform designs 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 possible RFI interference producers can advertise the frequencies in
use and consumers can use this information to avoid using these frequencies for
sensitive features.
The whole patch set is based on Linux 6.5-rc5. With some brief introductions
as below:
Patch1 - 2: Core functionality setup for WBRF feature support
Patch3 - 4: Bring WBRF support to wifi subsystem.
Patch5 - 9: Bring WBRF support to AMD graphics driver.
Evan Quan (9):
drivers core: Add support for Wifi band RF mitigations
drivers core: add ACPI based WBRF mechanism introduced by AMD
cfg80211: expose nl80211_chan_width_to_mhz for wide sharing
wifi: mac80211: Add support for WBRF features
drm/amd/pm: update driver_if and ppsmc headers for coming wbrf feature
drm/amd/pm: setup the framework to support Wifi RFI mitigation feature
drm/amd/pm: add flood detection for wbrf events
drm/amd/pm: enable Wifi RFI mitigation feature support for SMU13.0.0
drm/amd/pm: enable Wifi RFI mitigation feature support for SMU13.0.7
.../admin-guide/kernel-parameters.txt | 8 +
drivers/acpi/Makefile | 2 +
drivers/acpi/amd_wbrf.c | 294 ++++++++++++++
drivers/base/Kconfig | 20 +
drivers/base/Makefile | 1 +
drivers/base/wbrf.c | 367 ++++++++++++++++++
drivers/gpu/drm/amd/amdgpu/amdgpu.h | 2 +
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 17 +
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c | 213 ++++++++++
drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h | 33 ++
.../inc/pmfw_if/smu13_driver_if_v13_0_0.h | 14 +-
.../inc/pmfw_if/smu13_driver_if_v13_0_7.h | 14 +-
.../pm/swsmu/inc/pmfw_if/smu_v13_0_0_ppsmc.h | 3 +-
.../pm/swsmu/inc/pmfw_if/smu_v13_0_7_ppsmc.h | 3 +-
drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h | 3 +-
drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h | 3 +
.../gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c | 9 +
.../drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c | 60 +++
.../drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c | 59 +++
drivers/gpu/drm/amd/pm/swsmu/smu_internal.h | 3 +
include/linux/acpi_amd_wbrf.h | 25 ++
include/linux/ieee80211.h | 1 +
include/linux/wbrf.h | 47 +++
include/net/cfg80211.h | 8 +
net/mac80211/Makefile | 2 +
net/mac80211/chan.c | 9 +
net/mac80211/ieee80211_i.h | 9 +
net/mac80211/main.c | 2 +
net/mac80211/wbrf.c | 103 +++++
net/wireless/chan.c | 3 +-
30 files changed, 1331 insertions(+), 6 deletions(-)
create mode 100644 drivers/acpi/amd_wbrf.c
create mode 100644 drivers/base/wbrf.c
create mode 100644 include/linux/acpi_amd_wbrf.h
create mode 100644 include/linux/wbrf.h
create mode 100644 net/mac80211/wbrf.c
--
2.34.1
From: Evan Quan <hidden> Date: 2023-08-18 03:28:22
Due to electrical and mechanical constraints in certain platform designs
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 this, AMD has introduced a mechanism that devices can use to
notify active use of particular frequencies so that other devices can make
relative internal adjustments as necessary to avoid this resonance.
In order for a device to support this, the expected flow for device
driver or subsystems:
Drivers/subsystems contributing frequencies:
1) During probe, check `wbrf_supported_producer` to see if WBRF supported
for the device.
2) If adding frequencies, then call `wbrf_add_exclusion` with the
start and end ranges of the frequencies.
3) If removing frequencies, then call `wbrf_remove_exclusion` with
start and end ranges of the frequencies.
Drivers/subsystems responding to frequencies:
1) During probe, check `wbrf_supported_consumer` to see if WBRF is supported
for the device.
2) Call the `wbrf_register_notifier` to register for notifications of
frequency changes from other devices.
3) Call the `wbrf_retrieve_exclusions` to retrieve the current exclusions
range on receiving a notification and response correspondingly.
Meanwhile a kernel parameter `wbrf` with default setting as "auto" is
introduced to specify what the policy is.
- With `wbrf=on`, the WBRF features will be enabled forcely.
- With `wbrf=off`, the WBRF features will be disabled forcely.
- With `wbrf=auto`, it will be up to the system to do proper checks
to determine the WBRF features should be enabled or not.
Co-developed-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Co-developed-by: Evan Quan <redacted>
Signed-off-by: Evan Quan <redacted>
--
v4->v5:
- promote this to be a more generic solution with input argument taking
`struct device` and provide better scalability to support non-ACPI
scenarios(Andrew)
- update the APIs naming and some other minor fixes(Rafael)
v6->v7:
- revised the `struct wbrf_ranges_out` to be naturally aligned(Andrew)
- revised some code comments(Andrew)
v8->v9:
- update the document to be more readable(Randy)
---
.../admin-guide/kernel-parameters.txt | 8 +
drivers/base/Makefile | 1 +
drivers/base/wbrf.c | 280 ++++++++++++++++++
include/linux/wbrf.h | 47 +++
4 files changed, 336 insertions(+)
create mode 100644 drivers/base/wbrf.c
create mode 100644 include/linux/wbrf.h
@@ -7152,3 +7152,11 @@ xmon commands. off xmon is disabled.+ wbrf= [KNL]+ Format: { on | auto (default) | off }+ Controls if WBRF features should be forced on or off.+ on Force enable the WBRF features.+ auto Up to the system to do proper checks to+ determine the WBRF features should be enabled+ or not.+ off Force disable the WBRF features.
@@ -0,0 +1,47 @@+/* SPDX-License-Identifier: GPL-2.0 */+/*+*WifiBandExclusionInterface+*Copyright(C)2023AdvancedMicroDevices+*/++#ifndef _LINUX_WBRF_H+#define _LINUX_WBRF_H++#include<linux/device.h>++/* Maximum number of wbrf ranges */+#define MAX_NUM_OF_WBRF_RANGES 11++structexclusion_range{+/* start and end point of the frequency range in Hz */+u64start;+u64end;+};++structwbrf_ranges_in{+/* valid entry: `start` and `end` filled with non-zero values */+structexclusion_rangeband_list[MAX_NUM_OF_WBRF_RANGES];+};++structwbrf_ranges_out{+u64num_of_ranges;+structexclusion_rangeband_list[MAX_NUM_OF_WBRF_RANGES];+};++enumwbrf_notifier_actions{+WBRF_CHANGED,+};++boolwbrf_supported_producer(structdevice*dev);+intwbrf_add_exclusion(structdevice*adev,+structwbrf_ranges_in*in);+intwbrf_remove_exclusion(structdevice*dev,+structwbrf_ranges_in*in);+intwbrf_retrieve_exclusions(structdevice*dev,+structwbrf_ranges_out*out);+boolwbrf_supported_consumer(structdevice*dev);++intwbrf_register_notifier(structnotifier_block*nb);+intwbrf_unregister_notifier(structnotifier_block*nb);++#endif /* _LINUX_WBRF_H */
From: Evan Quan <hidden> Date: 2023-08-18 03:28:54
AMD has introduced an ACPI based mechanism to support WBRF for some
platforms with AMD dGPU + WLAN. This needs support from BIOS equipped
with necessary AML implementations and dGPU firmwares.
For those systems without the ACPI mechanism and developing solutions,
user can use/fall-back the generic WBRF solution for diagnosing potential
interference issues.
And for the platform which does not equip with the necessary AMD ACPI
implementations but with CONFIG_WBRF_AMD_ACPI built as 'y', it will
fall back to generic WBRF solution if the `wbrf` is set as "on".
Co-developed-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Co-developed-by: Evan Quan <redacted>
Signed-off-by: Evan Quan <redacted>
--
v4->v5:
- promote this to be a more generic solution with input argument taking
`struct device` and provide better scalability to support non-ACPI
scenarios(Andrew)
- update the APIs naming and some other minor fixes(Rafael)
v5->v6:
- make the code more readable and some other fixes(Andrew)
v6->v8:
- drop CONFIG_WBRF_GENERIC(Mario)
- add `wbrf` kernel parameter for policy control(Mario)
v8->v9:
- correct some coding style(Simon)
---
drivers/acpi/Makefile | 2 +
drivers/acpi/amd_wbrf.c | 294 ++++++++++++++++++++++++++++++++++
drivers/base/Kconfig | 20 +++
drivers/base/wbrf.c | 135 +++++++++++++---
include/linux/acpi_amd_wbrf.h | 25 +++
5 files changed, 452 insertions(+), 24 deletions(-)
create mode 100644 drivers/acpi/amd_wbrf.c
create mode 100644 include/linux/acpi_amd_wbrf.h
From: Evan Quan <hidden> Date: 2023-08-18 03:29:26
To support the WBRF mechanism, Wifi adapters utilized in the system must
register the frequencies in use(or unregister those frequencies no longer
used) via the dedicated calls. So that, other drivers responding to the
frequencies can take proper actions to mitigate possible interference.
Co-developed-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Co-developed-by: Evan Quan <redacted>
Signed-off-by: Evan Quan <redacted>
--
v1->v2:
- place the new added member(`wbrf_supported`) in
ieee80211_local(Johannes)
- handle chandefs change scenario properly(Johannes)
- some minor fixes around code sharing and possible invalid input
checks(Johannes)
v2->v3:
- drop unnecessary input checks and intermediate APIs(Mario)
- Separate some mac80211 common code(Mario, Johannes)
v3->v4:
- some minor fixes around return values(Johannes)
---
include/linux/ieee80211.h | 1 +
net/mac80211/Makefile | 2 +
net/mac80211/chan.c | 9 ++++
net/mac80211/ieee80211_i.h | 9 ++++
net/mac80211/main.c | 2 +
net/mac80211/wbrf.c | 103 +++++++++++++++++++++++++++++++++++++
6 files changed, 126 insertions(+)
create mode 100644 net/mac80211/wbrf.c
@@ -0,0 +1,103 @@+// SPDX-License-Identifier: GPL-2.0+/*+*WifiBandExclusionInterfaceforWWAN+*Copyright(C)2023AdvancedMicroDevices+*+*/++#include<linux/wbrf.h>+#include<net/cfg80211.h>+#include"ieee80211_i.h"++voidieee80211_check_wbrf_support(structieee80211_local*local)+{+structwiphy*wiphy=local->hw.wiphy;+structdevice*dev;++if(!wiphy)+return;++dev=wiphy->dev.parent;+if(!dev)+return;++local->wbrf_supported=wbrf_supported_producer(dev);+dev_dbg(dev,"WBRF is %s supported\n",+local->wbrf_supported?"":"not");+}++staticvoidget_chan_freq_boundary(u32center_freq,+u32bandwidth,+u64*start,+u64*end)+{+bandwidth=MHZ_TO_KHZ(bandwidth);+center_freq=MHZ_TO_KHZ(center_freq);++*start=center_freq-bandwidth/2;+*end=center_freq+bandwidth/2;++/* Frequency in HZ is expected */+*start=KHZ_TO_HZ(*start);+*end=KHZ_TO_HZ(*end);+}++staticvoidwbrf_get_ranges_from_chandef(structcfg80211_chan_def*chandef,+structwbrf_ranges_in*ranges_in)+{+u64start_freq1,end_freq1;+u64start_freq2,end_freq2;+intbandwidth;++bandwidth=nl80211_chan_width_to_mhz(chandef->width);++get_chan_freq_boundary(chandef->center_freq1,+bandwidth,+&start_freq1,+&end_freq1);++ranges_in->band_list[0].start=start_freq1;+ranges_in->band_list[0].end=end_freq1;++if(chandef->width==NL80211_CHAN_WIDTH_80P80){+get_chan_freq_boundary(chandef->center_freq2,+bandwidth,+&start_freq2,+&end_freq2);++ranges_in->band_list[1].start=start_freq2;+ranges_in->band_list[1].end=end_freq2;+}+}++voidieee80211_add_wbrf(structieee80211_local*local,+structcfg80211_chan_def*chandef)+{+structwbrf_ranges_inranges_in={0};+structdevice*dev;++if(!local->wbrf_supported)+return;++dev=local->hw.wiphy->dev.parent;++wbrf_get_ranges_from_chandef(chandef,&ranges_in);++wbrf_add_exclusion(dev,&ranges_in);+}++voidieee80211_remove_wbrf(structieee80211_local*local,+structcfg80211_chan_def*chandef)+{+structwbrf_ranges_inranges_in={0};+structdevice*dev;++if(!local->wbrf_supported)+return;++dev=local->hw.wiphy->dev.parent;++wbrf_get_ranges_from_chandef(chandef,&ranges_in);++wbrf_remove_exclusion(dev,&ranges_in);+}
From: Evan Quan <hidden> Date: 2023-08-18 03:29:26
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).
Signed-off-by: Evan Quan <redacted>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
--
v1->v2:
- update the prompt for feature support(Lijo)
v8->v9:
- update parameter document for smu_wbrf_event_handler(Simon)
---
drivers/gpu/drm/amd/amdgpu/amdgpu.h | 2 +
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 17 ++
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c | 194 ++++++++++++++++++
drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h | 23 +++
drivers/gpu/drm/amd/pm/swsmu/smu_internal.h | 3 +
5 files changed, 239 insertions(+)
@@ -195,6 +195,7 @@ int amdgpu_use_xgmi_p2p = 1;intamdgpu_vcnfw_log;intamdgpu_sg_display=-1;/* auto */intamdgpu_user_partt_mode=AMDGPU_AUTO_COMPUTE_PARTITION_MODE;+intamdgpu_wbrf=-1;staticvoidamdgpu_drv_delayed_reset_work_handler(structwork_struct*work);
@@ -981,6 +982,22 @@ module_param_named(user_partt_mode, amdgpu_user_partt_mode, uint, 0444);module_param(enforce_isolation,bool,0444);MODULE_PARM_DESC(enforce_isolation,"enforce process isolation between graphics and compute . enforce_isolation = on");+/**+*DOC:wbrf(int)+*EnableWifiRFIinterferencemitigationfeature.+*Duetoelectricalandmechanicalconstraintstheremaybelikelyinterferenceof+*relativelyhigh-poweredharmonicsofthe(G-)DDRmemoryclockswithlocalradio+*modulefrequencybandsusedbyWifi6/6e/7.TomitigatethepossibleRFIinterference,+*withthisfeatureenabled,PMFWwilluseeither“shadowedP-State”or“P-State”based+*onactivelistoffrequenciesin-use(tobeavoided)aspartofinitialsettingor+*P-statetransition.However,theremaybepotentialperformanceimpactwiththis+*featureenabled.+*(0=disabled,1=enabled,-1=auto(defaultsetting,willbeenabledifsupported))+*/+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.*Theyaresupportedbythemach64,r128,radeondrivers*/
@@ -1228,6 +1228,173 @@ static int smu_get_thermal_temperature_range(struct smu_context *smu)returnret;}+/**+*smu_wbrf_handle_exclusion_ranges-consumethewbrfexclusionranges+*+*@smu:smu_contextpointer+*+*RetrievethewbrfexclusionrangesandsendthemtoPMFWforproperhandling.+*Returns0onsuccess,erroronfailure.+*/+staticintsmu_wbrf_handle_exclusion_ranges(structsmu_context*smu)+{+structwbrf_ranges_outwbrf_exclusion={0};+structexclusion_range*wifi_bands=wbrf_exclusion.band_list;+structamdgpu_device*adev=smu->adev;+uint64_tstart,end;+intret,i,j;++ret=wbrf_retrieve_exclusions(adev->dev,&wbrf_exclusion);+if(ret){+dev_err(adev->dev,"Failed to retrieve exclusion ranges!\n");+returnret;+}++/*+*Theexclusionrangesarraywegotmightbefilledwithholesandduplicate+*entries.Forexample:+*{(2400,2500),(0,0),(6882,6962),(2400,2500),(0,0),(6117,6189),(0,0)...}+*Weneedtodosomesortupstoeliminatethoseholesandduplicateentries.+*Expectedoutput:{(2400,2500),(6117,6189),(6882,6962),(0,0)...}+*/+for(i=0;i<MAX_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=MAX_NUM_OF_WBRF_RANGES-1;j>i;j--)+if(wifi_bands[j].start&&+wifi_bands[j].end)+break;++if(j>i){+wifi_bands[i].start=wifi_bands[j].start;+wifi_bands[i].end=wifi_bands[j].end;+wifi_bands[j].start=0;+wifi_bands[j].end=0;+}++continue;+}++/* eliminate duplicate entries */+for(j=i+1;j<MAX_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;+continue;+}+}+}++/* Send the sorted wifi_bands to PMFW */+ret=smu_set_wbrf_exclusion_ranges(smu,wifi_bands);+/* Give it another chance */+if(unlikely(ret==-EBUSY)){+mdelay(5);+ret=smu_set_wbrf_exclusion_ranges(smu,wifi_bands);+}++returnret;+}++/**+*smu_wbrf_event_handler-handlenotifyevents+*+*@nb:notifierblock+*@action:eventtype+*@_arg:eventdata+*+*Callsrelevantamdgpufunctioninresponsetowbrfevent+*notificationfromkernel.+*/+staticintsmu_wbrf_event_handler(structnotifier_block*nb,+unsignedlongaction,void*_arg)+{+structsmu_context*smu=container_of(nb,structsmu_context,+wbrf_notifier);++switch(action){+caseWBRF_CHANGED:+smu_wbrf_handle_exclusion_ranges(smu);+break;+default:+returnNOTIFY_DONE;+};++returnNOTIFY_OK;+}++/**+*smu_wbrf_support_check-checkwbrfsupport+*+*@smu:smu_contextpointer+*+*VerifiestheACPIinterfacewhetherwbrfissupported.+*/+staticvoidsmu_wbrf_support_check(structsmu_context*smu)+{+structamdgpu_device*adev=smu->adev;++smu->wbrf_supported=smu_is_asic_wbrf_supported(smu)&&+!!amdgpu_wbrf&&+wbrf_supported_consumer(adev->dev);++if(smu->wbrf_supported)+dev_info(adev->dev,"RF interference mitigation is supported\n");+}++/**+*smu_wbrf_init-initdriverwbrfsupport+*+*@smu:smu_contextpointer+*+*VerifiestheAMDACPIinterfacesandregisterswiththewbrf+*notifierchainifwbrffeatureissupported.+*Returns0onsuccess,erroronfailure.+*/+staticintsmu_wbrf_init(structsmu_context*smu)+{+structamdgpu_device*adev=smu->adev;+intret;++if(!smu->wbrf_supported)+return0;++smu->wbrf_notifier.notifier_call=smu_wbrf_event_handler;+ret=wbrf_register_notifier(&smu->wbrf_notifier);+if(ret)+returnret;++/*+*Somewifibandexclusionrangesmaybealreadythere+*beforeourdriverloaded.Tomakesureourdriver+*isawaredofthoseexclusionranges.+*/+ret=smu_wbrf_handle_exclusion_ranges(smu);+if(ret)+dev_err(adev->dev,"Failed to handle wbrf exclusion ranges\n");++returnret;+}++/**+*smu_wbrf_fini-teardowndriverwbrfsupport+*+*@smu:smu_contextpointer+*+*Unregisterswiththewbrfnotifierchain.+*/+staticvoidsmu_wbrf_fini(structsmu_context*smu)+{+if(!smu->wbrf_supported)+return;++wbrf_unregister_notifier(&smu->wbrf_notifier);+}+staticintsmu_smc_hw_setup(structsmu_context*smu){structsmu_feature*feature=&smu->smu_feature;
@@ -1320,6 +1487,15 @@ static int smu_smc_hw_setup(struct smu_context *smu)if(ret)returnret;+/* 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");+returnret;+}+}+/**WithSCPMenabled,theseactions(andrelevantmessages)are*notneededandpermitted.
@@ -1416,6 +1592,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");+returnret;+}++/* Init wbrf support. Properly setup the notifier */+ret=smu_wbrf_init(smu);+if(ret)+dev_err(adev->dev,"Error during wbrf init call\n");returnret;}
@@ -1471,6 +1656,13 @@ static int smu_hw_init(void *handle)returnret;}+/*+*Checkwhetherwbrfissupported.Thisneedstobedone+*beforeSMUsetupstartssincepartofSMUconfiguration+*reliesonthis.+*/+smu_wbrf_support_check(smu);+if(smu->is_apu){ret=smu_set_gfx_imu_enable(smu);if(ret)
@@ -1623,6 +1815,8 @@ static int smu_smc_hw_cleanup(struct smu_context *smu)structamdgpu_device*adev=smu->adev;intret=0;+smu_wbrf_fini(smu);+cancel_work_sync(&smu->throttling_logging_work);cancel_work_sync(&smu->interrupt_work);
@@ -575,6 +577,10 @@ struct smu_contextu32debug_resp_reg;structdelayed_workswctf_delayed_work;++/* data structures for wbrf feature support */+boolwbrf_supported;+structnotifier_blockwbrf_notifier;};structi2c_adapter;
@@ -581,6 +587,7 @@ struct smu_context/* data structures for wbrf feature support */boolwbrf_supported;structnotifier_blockwbrf_notifier;+structdelayed_workwbrf_delayed_work;};structi2c_adapter;
@@ -481,6 +483,9 @@ static int smu_v13_0_0_tables_init(struct smu_context *smu)PAGE_SIZE,AMDGPU_GEM_DOMAIN_VRAM);SMU_TABLE_INIT(tables,SMU_TABLE_ECCINFO,sizeof(EccInfoTable_t),PAGE_SIZE,AMDGPU_GEM_DOMAIN_VRAM);+SMU_TABLE_INIT(tables,SMU_TABLE_WIFIBAND,+sizeof(WifiBandEntryTable_t),PAGE_SIZE,+AMDGPU_GEM_DOMAIN_VRAM);smu_table->metrics_table=kzalloc(sizeof(SmuMetricsExternal_t),GFP_KERNEL);if(!smu_table->metrics_table)
@@ -2593,6 +2598,58 @@ static ssize_t smu_v13_0_0_get_ecc_info(struct smu_context *smu,returnret;}+staticboolsmu_v13_0_0_wbrf_support_check(structsmu_context*smu)+{+structamdgpu_device*adev=smu->adev;++switch(adev->ip_versions[MP1_HWIP][0]){+caseIP_VERSION(13,0,0):+returnsmu->smc_fw_version>=0x004e6300;+caseIP_VERSION(13,0,10):+returnsmu->smc_fw_version>=0x00503300;+default:+returnfalse;+}+}++staticintsmu_v13_0_0_set_wbrf_exclusion_ranges(structsmu_context*smu,+structexclusion_range*exclusion_ranges)+{+WifiBandEntryTable_twifi_bands;+intvalid_entries=0;+intret,i;++memset(&wifi_bands,0,sizeof(wifi_bands));+for(i=0;i<ARRAY_SIZE(wifi_bands.WifiBandEntry);i++){+if(!exclusion_ranges[i].start&&+!exclusion_ranges[i].end)+break;++/* PMFW expects the inputs to be in Mhz unit */+wifi_bands.WifiBandEntry[valid_entries].LowFreq=+DIV_ROUND_DOWN_ULL(exclusion_ranges[i].start,HZ_IN_MHZ);+wifi_bands.WifiBandEntry[valid_entries++].HighFreq=+DIV_ROUND_UP_ULL(exclusion_ranges[i].end,HZ_IN_MHZ);+}+wifi_bands.WifiBandEntryNum=valid_entries;++/*+*PerconfirmwithPMFWteam,WifiBandEntryNum=0+*isavalidsetting.So,thereshouldbenodirect+*returnonthat.+*/++ret=smu_cmn_update_table(smu,+SMU_TABLE_WIFIBAND,+0,+(void*)(&wifi_bands),+true);+if(ret)+dev_err(smu->adev->dev,"Failed to set wifiband!");++returnret;+}+staticconststructpptable_funcssmu_v13_0_0_ppt_funcs={.get_allowed_feature_mask=smu_v13_0_0_get_allowed_feature_mask,.set_default_dpm_table=smu_v13_0_0_set_default_dpm_table,
@@ -497,6 +499,9 @@ static int smu_v13_0_7_tables_init(struct smu_context *smu)AMDGPU_GEM_DOMAIN_VRAM);SMU_TABLE_INIT(tables,SMU_TABLE_COMBO_PPTABLE,MP0_MP1_DATA_REGION_SIZE_COMBOPPTABLE,PAGE_SIZE,AMDGPU_GEM_DOMAIN_VRAM);+SMU_TABLE_INIT(tables,SMU_TABLE_WIFIBAND,+sizeof(WifiBandEntryTable_t),PAGE_SIZE,+AMDGPU_GEM_DOMAIN_VRAM);smu_table->metrics_table=kzalloc(sizeof(SmuMetricsExternal_t),GFP_KERNEL);if(!smu_table->metrics_table)
@@ -2173,6 +2178,57 @@ static int smu_v13_0_7_set_df_cstate(struct smu_context *smu,NULL);}+staticboolsmu_v13_0_7_wbrf_support_check(structsmu_context*smu)+{+returnsmu->smc_fw_version>0x00524600;+}++staticintsmu_v13_0_7_set_wbrf_exclusion_ranges(structsmu_context*smu,+structexclusion_range*exclusion_ranges)+{+WifiBandEntryTable_twifi_bands;+intvalid_entries=0;+intret,i;++memset(&wifi_bands,0,sizeof(wifi_bands));+for(i=0;i<ARRAY_SIZE(wifi_bands.WifiBandEntry);i++){+if(!exclusion_ranges[i].start&&+!exclusion_ranges[i].end)+break;++/* PMFW expects the inputs to be in Mhz unit */+wifi_bands.WifiBandEntry[valid_entries].LowFreq=+DIV_ROUND_DOWN_ULL(exclusion_ranges[i].start,HZ_IN_MHZ);+wifi_bands.WifiBandEntry[valid_entries++].HighFreq=+DIV_ROUND_UP_ULL(exclusion_ranges[i].end,HZ_IN_MHZ);+}+wifi_bands.WifiBandEntryNum=valid_entries;++/*+*PerconfirmwithPMFWteam,WifiBandEntryNum=0isavalidsetting.+*Consideringthescenariosbelow:+*-Atfirstthewifideviceaddsanexclusionrangee.g.(2400,2500)to+*BIOSandourdrivergetsnotified.WewillsetWifiBandEntryNum=1+*andpasstheWifiBandEntry(2400,2500)toPMFW.+*+*-Laterthewifideviceremovesthewifibandlistaddedaboveand+*ourdrivergetsnotifiedagain.Atthistime,driverwillset+*WifiBandEntryNum=0andpassanemptyWifiBandEntrylisttoPMFW.+*-PMFWmaystillneedtodosomeuclkshadowupdate(e.g.switching+*fromshadowclockbacktoprimaryclock)onreceivingthis.+*/++ret=smu_cmn_update_table(smu,+SMU_TABLE_WIFIBAND,+0,+(void*)(&wifi_bands),+true);+if(ret)+dev_err(smu->adev->dev,"Failed to set wifiband!");++returnret;+}+staticconststructpptable_funcssmu_v13_0_7_ppt_funcs={.get_allowed_feature_mask=smu_v13_0_7_get_allowed_feature_mask,.set_default_dpm_table=smu_v13_0_7_set_default_dpm_table,
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).
Signed-off-by: Evan Quan <redacted>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
--
v1->v2:
- update the prompt for feature support(Lijo)
v8->v9:
- update parameter document for smu_wbrf_event_handler(Simon)
---
drivers/gpu/drm/amd/amdgpu/amdgpu.h | 2 +
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 17 ++
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c | 194 ++++++++++++++++++
drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h | 23 +++
drivers/gpu/drm/amd/pm/swsmu/smu_internal.h | 3 +
5 files changed, 239 insertions(+)
@@ -195,6 +195,7 @@ int amdgpu_use_xgmi_p2p = 1;intamdgpu_vcnfw_log;intamdgpu_sg_display=-1;/* auto */intamdgpu_user_partt_mode=AMDGPU_AUTO_COMPUTE_PARTITION_MODE;+intamdgpu_wbrf=-1;staticvoidamdgpu_drv_delayed_reset_work_handler(structwork_struct*work);
@@ -981,6 +982,22 @@ module_param_named(user_partt_mode, amdgpu_user_partt_mode, uint, 0444);module_param(enforce_isolation,bool,0444);MODULE_PARM_DESC(enforce_isolation,"enforce process isolation between graphics and compute . enforce_isolation = on");+/**+*DOC:wbrf(int)+*EnableWifiRFIinterferencemitigationfeature.+*Duetoelectricalandmechanicalconstraintstheremaybelikelyinterferenceof+*relativelyhigh-poweredharmonicsofthe(G-)DDRmemoryclockswithlocalradio+*modulefrequencybandsusedbyWifi6/6e/7.TomitigatethepossibleRFIinterference,+*withthisfeatureenabled,PMFWwilluseeither“shadowedP-State”or“P-State”based+*onactivelistoffrequenciesin-use(tobeavoided)aspartofinitialsettingor+*P-statetransition.However,theremaybepotentialperformanceimpactwiththis+*featureenabled.+*(0=disabled,1=enabled,-1=auto(defaultsetting,willbeenabledifsupported))+*/+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.*Theyaresupportedbythemach64,r128,radeondrivers*/
@@ -1228,6 +1228,173 @@ static int smu_get_thermal_temperature_range(struct smu_context *smu)returnret;}+/**+*smu_wbrf_handle_exclusion_ranges-consumethewbrfexclusionranges+*+*@smu:smu_contextpointer+*+*RetrievethewbrfexclusionrangesandsendthemtoPMFWforproperhandling.+*Returns0onsuccess,erroronfailure.+*/+staticintsmu_wbrf_handle_exclusion_ranges(structsmu_context*smu)+{+structwbrf_ranges_outwbrf_exclusion={0};+structexclusion_range*wifi_bands=wbrf_exclusion.band_list;+structamdgpu_device*adev=smu->adev;+uint64_tstart,end;+intret,i,j;++ret=wbrf_retrieve_exclusions(adev->dev,&wbrf_exclusion);+if(ret){+dev_err(adev->dev,"Failed to retrieve exclusion ranges!\n");+returnret;+}++/*+*Theexclusionrangesarraywegotmightbefilledwithholesandduplicate+*entries.Forexample:+*{(2400,2500),(0,0),(6882,6962),(2400,2500),(0,0),(6117,6189),(0,0)...}+*Weneedtodosomesortupstoeliminatethoseholesandduplicateentries.+*Expectedoutput:{(2400,2500),(6117,6189),(6882,6962),(0,0)...}+*/+for(i=0;i<MAX_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=MAX_NUM_OF_WBRF_RANGES-1;j>i;j--)+if(wifi_bands[j].start&&+wifi_bands[j].end)+break;++if(j>i){+wifi_bands[i].start=wifi_bands[j].start;+wifi_bands[i].end=wifi_bands[j].end;
Since the last value is now 0, one way to optimize is to keep max as j
-1 for the next iteration.
If continued at this point, it won't eliminate all duplicates.
Example:
(2000,2100)(0,0)(1100,1200)(0, 0)(2500,2600)(3000,3200)(1100,1200)
Once it places (1100,1200) at index 1, it will continue the loop and
then it skips duplicate entry at index 2.
Once replaced, you may assign the new start/end
start = wifi_bands[i].start = wifi_bands[j].start;
end= wifi_bands[i].end = wifi_bands[j].end;
and then proceed to the duplicate entries scan loop below.
I think this'continue' is not required.
Thanks,
Lijo
quoted hunk
+ }
+ }
+ }
+
+ /* Send the sorted wifi_bands to PMFW */
+ ret = smu_set_wbrf_exclusion_ranges(smu, wifi_bands);
+ /* Give it another chance */
+ 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 &&
+ 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 = 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;
+
+ wbrf_unregister_notifier(&smu->wbrf_notifier);
+}
+
static int smu_smc_hw_setup(struct smu_context *smu)
{
struct smu_feature *feature = &smu->smu_feature;
@@ -1320,6 +1487,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.
@@ -1416,6 +1592,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; }
@@ -1471,6 +1656,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)
@@ -1623,6 +1815,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);
@@ -575,6 +577,10 @@ struct smu_contextu32debug_resp_reg;structdelayed_workswctf_delayed_work;++/* data structures for wbrf feature support */+boolwbrf_supported;+structnotifier_blockwbrf_notifier;};structi2c_adapter;
This is one-time and can be part of sw_init.
Thanks,
Lijo
quoted hunk
smu->wbrf_notifier.notifier_call = smu_wbrf_event_handler;
ret = wbrf_register_notifier(&smu->wbrf_notifier);
if (ret)
@@ -1373,11 +1391,10 @@ static int smu_wbrf_init(struct smu_context *smu) * 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");+ schedule_delayed_work(&smu->wbrf_delayed_work,+ msecs_to_jiffies(SMU_WBRF_EVENT_HANDLING_PACE));- return ret;+ return 0; } /**
@@ -581,6 +587,7 @@ struct smu_context/* data structures for wbrf feature support */boolwbrf_supported;structnotifier_blockwbrf_notifier;+structdelayed_workwbrf_delayed_work;};structi2c_adapter;
@@ -481,6 +483,9 @@ static int smu_v13_0_0_tables_init(struct smu_context *smu)PAGE_SIZE,AMDGPU_GEM_DOMAIN_VRAM);SMU_TABLE_INIT(tables,SMU_TABLE_ECCINFO,sizeof(EccInfoTable_t),PAGE_SIZE,AMDGPU_GEM_DOMAIN_VRAM);+SMU_TABLE_INIT(tables,SMU_TABLE_WIFIBAND,+sizeof(WifiBandEntryTable_t),PAGE_SIZE,+AMDGPU_GEM_DOMAIN_VRAM);smu_table->metrics_table=kzalloc(sizeof(SmuMetricsExternal_t),GFP_KERNEL);if(!smu_table->metrics_table)
@@ -2593,6 +2598,58 @@ static ssize_t smu_v13_0_0_get_ecc_info(struct smu_context *smu,returnret;}+staticboolsmu_v13_0_0_wbrf_support_check(structsmu_context*smu)+{+structamdgpu_device*adev=smu->adev;++switch(adev->ip_versions[MP1_HWIP][0]){+caseIP_VERSION(13,0,0):+returnsmu->smc_fw_version>=0x004e6300;+caseIP_VERSION(13,0,10):+returnsmu->smc_fw_version>=0x00503300;+default:+returnfalse;+}+}++staticintsmu_v13_0_0_set_wbrf_exclusion_ranges(structsmu_context*smu,+structexclusion_range*exclusion_ranges)+{+WifiBandEntryTable_twifi_bands;+intvalid_entries=0;+intret,i;++memset(&wifi_bands,0,sizeof(wifi_bands));+for(i=0;i<ARRAY_SIZE(wifi_bands.WifiBandEntry);i++){+if(!exclusion_ranges[i].start&&+!exclusion_ranges[i].end)+break;++/* PMFW expects the inputs to be in Mhz unit */+wifi_bands.WifiBandEntry[valid_entries].LowFreq=+DIV_ROUND_DOWN_ULL(exclusion_ranges[i].start,HZ_IN_MHZ);+wifi_bands.WifiBandEntry[valid_entries++].HighFreq=+DIV_ROUND_UP_ULL(exclusion_ranges[i].end,HZ_IN_MHZ);+}+wifi_bands.WifiBandEntryNum=valid_entries;++/*+*PerconfirmwithPMFWteam,WifiBandEntryNum=0+*isavalidsetting.So,thereshouldbenodirect+*returnonthat.+*/++ret=smu_cmn_update_table(smu,+SMU_TABLE_WIFIBAND,+0,+(void*)(&wifi_bands),+true);+if(ret)+dev_err(smu->adev->dev,"Failed to set wifiband!");
Since system could be operational even after this, can we downgrade this?
Thanks,
Lijo
From: "Rafael J. Wysocki" <rafael@kernel.org> Date: 2023-08-18 16:43:01
On Fri, Aug 18, 2023 at 5:27 AM Evan Quan [off-list ref] wrote:
Due to electrical and mechanical constraints in certain platform designs
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 this, AMD has introduced a mechanism that devices can use to
notify active use of particular frequencies so that other devices can make
relative internal adjustments as necessary to avoid this resonance.
In order for a device to support this, the expected flow for device
driver or subsystems:
Drivers/subsystems contributing frequencies:
1) During probe, check `wbrf_supported_producer` to see if WBRF supported
for the device.
2) If adding frequencies, then call `wbrf_add_exclusion` with the
start and end ranges of the frequencies.
3) If removing frequencies, then call `wbrf_remove_exclusion` with
start and end ranges of the frequencies.
Drivers/subsystems responding to frequencies:
1) During probe, check `wbrf_supported_consumer` to see if WBRF is supported
for the device.
2) Call the `wbrf_register_notifier` to register for notifications of
frequency changes from other devices.
3) Call the `wbrf_retrieve_exclusions` to retrieve the current exclusions
range on receiving a notification and response correspondingly.
Meanwhile a kernel parameter `wbrf` with default setting as "auto" is
introduced to specify what the policy is.
- With `wbrf=on`, the WBRF features will be enabled forcely.
- With `wbrf=off`, the WBRF features will be disabled forcely.
- With `wbrf=auto`, it will be up to the system to do proper checks
to determine the WBRF features should be enabled or not.
Co-developed-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Co-developed-by: Evan Quan <redacted>
Signed-off-by: Evan Quan <redacted>
In the first place, this requires quite a bit of driver API
documentation that is missing.
To a minimum, it should explain what the interface is for and how it
is supposed to be used by drivers (both "producers" and "consumers").
And how to determine whether or not a given device is a "producer" or
"consumer" from the WBRF perspective.
quoted hunk
--
v4->v5:
- promote this to be a more generic solution with input argument taking
`struct device` and provide better scalability to support non-ACPI
scenarios(Andrew)
- update the APIs naming and some other minor fixes(Rafael)
v6->v7:
- revised the `struct wbrf_ranges_out` to be naturally aligned(Andrew)
- revised some code comments(Andrew)
v8->v9:
- update the document to be more readable(Randy)
---
.../admin-guide/kernel-parameters.txt | 8 +
drivers/base/Makefile | 1 +
drivers/base/wbrf.c | 280 ++++++++++++++++++
include/linux/wbrf.h | 47 +++
4 files changed, 336 insertions(+)
create mode 100644 drivers/base/wbrf.c
create mode 100644 include/linux/wbrf.h
@@ -7152,3 +7152,11 @@ xmon commands. off xmon is disabled.+ wbrf= [KNL]+ Format: { on | auto (default) | off }+ Controls if WBRF features should be forced on or off.+ on Force enable the WBRF features.+ auto Up to the system to do proper checks to+ determine the WBRF features should be enabled+ or not.+ off Force disable the WBRF features.
Well, how's a casual reader of this file supposed to find out what
WBRF is and whether or not they should care?
I would expect some explanation of the interface design and purpose here.
So I don't have to wonder what WBRF_POLICY_MODE is or what the
"exclusion ranges" are below.
It never returns anything else. Should it be void?
+}
+
+static int _wbrf_retrieve_exclusion_ranges(struct wbrf_ranges_out *out)
+{
+ int out_idx = 0;
+ int i;
+
+ memset(out, 0, sizeof(*out));
+
+ for (i = 0; i < ARRAY_SIZE(wbrf_pool.band_list); i++) {
+ if (!wbrf_pool.band_list[i].start &&
+ !wbrf_pool.band_list[i].end)
+ continue;
+
+ out->band_list[out_idx].start = wbrf_pool.band_list[i].start;
+ out->band_list[out_idx++].end = wbrf_pool.band_list[i].end;
+ }
+
+ out->num_of_ranges = out_idx;
+
+ return 0;
Same here.
+}
+
+/**
+ * wbrf_supported_system - Determine if the system supports WBRF features
+ *
+ * WBRF is used to mitigate devices that cause harmonic interference.
+ * This function will determine if the platform is able to support the
+ * WBRF features.
The code doesn't quite match the description above. I guess the code
is temporary?
+ */
+static bool wbrf_supported_system(void)
+{
+ switch (wbrf_policy) {
+ case WBRF_POLICY_FORCE_ENABLE:
+ return true;
+ case WBRF_POLICY_FORCE_DISABLE:
+ return false;
+ case WBRF_POLICY_AUTO:
+ return false;
+ }
+
+ return false;
+}
+
+/**
+ * wbrf_supported_producer - Determine if the device should report frequencies
+ *
+ * @dev: device pointer
+ *
+ * WBRF is used to mitigate devices that cause harmonic interference.
+ * This function will determine if this device should report such frequencies.
It is not clear how "harmonic interference" is related to "such
frequencies" from the above.
+ */
+bool wbrf_supported_producer(struct device *dev)
+{
+ if (!wbrf_supported_system())
+ return false;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(wbrf_supported_producer);
+
+/**
+ * wbrf_add_exclusion - Add frequency ranges to the exclusion list
+ *
+ * @dev: device pointer
+ * @in: input structure containing the frequency ranges to be added
+ *
+ * Add frequencies into the exclusion list for supported consumers
+ * to react to.
Well, the above isn't particularly helpful IMV.
What's "the exclusion list"? What are "supported consumers" and how
are they going to "react" and to what exactly (the exclusion list or
the frequencies)?
Why is the notifier chain not mentioned in the kerneldoc description
of the function?
+ */
+int wbrf_add_exclusion(struct device *dev,
+ struct wbrf_ranges_in *in)
+{
+ int r;
+
+ mutex_lock(&wbrf_mutex);
+
+ r = _wbrf_add_exclusion_ranges(in);
+
+ mutex_unlock(&wbrf_mutex);
+ if (r)
+ return r;
+
+ blocking_notifier_call_chain(&wbrf_chain_head, WBRF_CHANGED, NULL);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wbrf_add_exclusion);
+
+/**
+ * wbrf_remove_exclusion - Remove frequency ranges from the exclusion list
+ *
+ * @dev: device pointer
+ * @in: input structure containing the frequency ranges to be removed
+ *
+ * Remove frequencies from the exclusion list for supported consumers
+ * to react to.
This has the same problems as the above.
+ */
+int wbrf_remove_exclusion(struct device *dev,
+ struct wbrf_ranges_in *in)
+{
+ int r;
+
+ mutex_lock(&wbrf_mutex);
+
+ r = _wbrf_remove_exclusion_ranges(in);
+
+ mutex_unlock(&wbrf_mutex);
+ if (r)
+ return r;
+
+ blocking_notifier_call_chain(&wbrf_chain_head, WBRF_CHANGED, NULL);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wbrf_remove_exclusion);
+
+/**
+ * wbrf_supported_consumer - Determine if the device should react to frequencies
+ *
+ * @dev: device pointer
+ *
+ * WBRF is used to mitigate devices that cause harmonic interference.
What does this mean? How can a device be "mitigated" and what
"harmonic interference" is this about?
+ * This function will determine if this device should react to reports from
+ * other devices for such frequencies.
What are "such frequencies"?
+ */
+bool wbrf_supported_consumer(struct device *dev)
+{
+ if (!wbrf_supported_system())
+ return false;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(wbrf_supported_consumer);
+
+/**
+ * wbrf_register_notifier - Register for notifications of frequency changes
+ *
+ * @nb: driver notifier block
+ *
+ * WBRF is used to mitigate devices that cause harmonic interference.
+ * This function will allow consumers to register for frequency notifications.
What's a "frequency notification"?
+ */
+int wbrf_register_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&wbrf_chain_head, nb);
+}
+EXPORT_SYMBOL_GPL(wbrf_register_notifier);
+
+/**
+ * wbrf_unregister_notifier - Unregister for notifications of frequency changes
+ *
+ * @nb: driver notifier block
+ *
+ * WBRF is used to mitigate devices that cause harmonic interference.
+ * This function will allow consumers to unregister for frequency notifications.
+ */
+int wbrf_unregister_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&wbrf_chain_head, nb);
+}
+EXPORT_SYMBOL_GPL(wbrf_unregister_notifier);
+
+/**
+ * wbrf_retrieve_exclusions - Retrieve the exclusion list
+ *
+ * @dev: device pointer
+ * @out: output structure containing the frequency ranges to be excluded
@@ -0,0 +1,47 @@+/* SPDX-License-Identifier: GPL-2.0 */+/*+*WifiBandExclusionInterface+*Copyright(C)2023AdvancedMicroDevices+*/++#ifndef _LINUX_WBRF_H+#define _LINUX_WBRF_H++#include<linux/device.h>++/* Maximum number of wbrf ranges */+#define MAX_NUM_OF_WBRF_RANGES 11++structexclusion_range{+/* start and end point of the frequency range in Hz */
I would put the comment above the whole struct definition and use the
kerneldoc format for it.
From: "Rafael J. Wysocki" <rafael@kernel.org> Date: 2023-08-18 17:42:35
On Fri, Aug 18, 2023 at 5:27 AM Evan Quan [off-list ref] wrote:
AMD has introduced an ACPI based mechanism to support WBRF for some
platforms with AMD dGPU + WLAN. This needs support from BIOS equipped
with necessary AML implementations and dGPU firmwares.
This needs a problem statement in the first place: What exactly caused
AMD to come up with this design?
For those systems without the ACPI mechanism and developing solutions,
user can use/fall-back the generic WBRF solution for diagnosing potential
interference issues.
And for the platform which does not equip with the necessary AMD ACPI
implementations but with CONFIG_WBRF_AMD_ACPI built as 'y', it will
fall back to generic WBRF solution if the `wbrf` is set as "on".
OK, so I suppose that the patch implements support for the AMD WBRF
firmware interface? That needs to be stated somewhere.
From patch reverse-engineering it looks like the generic WBRF code is
updated by it to hook up to the ACPI implementation if supported. If
my understanding is correct, it would be nice to state that in the
changelog too.
quoted hunk
Co-developed-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Co-developed-by: Evan Quan <redacted>
Signed-off-by: Evan Quan <redacted>
--
v4->v5:
- promote this to be a more generic solution with input argument taking
`struct device` and provide better scalability to support non-ACPI
scenarios(Andrew)
- update the APIs naming and some other minor fixes(Rafael)
v5->v6:
- make the code more readable and some other fixes(Andrew)
v6->v8:
- drop CONFIG_WBRF_GENERIC(Mario)
- add `wbrf` kernel parameter for policy control(Mario)
v8->v9:
- correct some coding style(Simon)
---
drivers/acpi/Makefile | 2 +
drivers/acpi/amd_wbrf.c | 294 ++++++++++++++++++++++++++++++++++
drivers/base/Kconfig | 20 +++
drivers/base/wbrf.c | 135 +++++++++++++---
include/linux/acpi_amd_wbrf.h | 25 +++
5 files changed, 452 insertions(+), 24 deletions(-)
create mode 100644 drivers/acpi/amd_wbrf.c
create mode 100644 include/linux/acpi_amd_wbrf.h
It would be nice to have a description of the firmware interface here,
at least in general terms.
In particular, the terminology used throughout the code must be explained.
Without it, qualifying the validity and/or usefulness of the code is
rather hard.
+ */
+
+#include <linux/acpi.h>
+#include <linux/acpi_amd_wbrf.h>
+
+#define ACPI_AMD_WBRF_METHOD "\\WBRF"
+
+/*
+ * Functions bit vector for WBRF method
+ *
+ * Bit 0: Supported for any functions other than function 0.
+ * Bit 1: Function 1 (Add / Remove frequency) is supported.
+ * Bit 2: Function 2 (Get frequency list) is supported.
+ */
+#define WBRF_ENABLED 0x0
+#define WBRF_RECORD 0x1
+#define WBRF_RETRIEVE 0x2
+
+/* record actions */
+#define WBRF_RECORD_ADD 0x0
+#define WBRF_RECORD_REMOVE 0x1
+
+#define WBRF_REVISION 0x1
+
+/*
+ * The data structure used for WBRF_RETRIEVE is not natually aligned.
The calling convention of this function isn't particularly straightforward.
Also, AFAICS, it has only one caller which passes NULL as the last
argument (which is not checked above when obj->type is
ACPI_TYPE_BUFFER, so I guess it's never been the case in practice) and
discards whatever is passed via arg4.
Why is arg4 even needed and why is the ACPI_TYPE_BUFFER case regarded
as a valid one?
+
+static int wbrf_record(struct acpi_device *adev, uint8_t action,
+ struct wbrf_ranges_in *in)
+{
+ union acpi_object argv4;
+ union acpi_object *tmp;
+ u32 num_of_ranges = 0;
+ u32 num_of_elements;
+ u32 arg_idx = 0;
+ u32 loop_idx;
+ int ret;
+
+ if (!in)
+ return -EINVAL;
+
+ for (loop_idx = 0; loop_idx < ARRAY_SIZE(in->band_list);
+ loop_idx++)
+ if (in->band_list[loop_idx].start &&
+ in->band_list[loop_idx].end)
+ num_of_ranges++;
+
+ /*
+ * Every range comes with two end points(start and end) and
I would do it the other way around, that is
if (ACPI_FAILURE(ret))
return NULL;
return buf.pointer;
and the pointer cast is not necessary, because buf.pointer is void anyway.
+}
+
+static bool check_acpi_wbrf(acpi_handle handle, u64 rev, u64 funcs)
+{
+ int i;
+ u64 mask = 0;
+ union acpi_object *obj;
+
+ if (funcs == 0)
+ return false;
+
+ obj = acpi_evaluate_wbrf(handle, rev, 0);
+ if (!obj)
+ return false;
+
+ if (obj->type != ACPI_TYPE_BUFFER)
+ return false;
+
+ /*
+ * Bit vector providing supported functions information.
+ * Each bit marks support for one specific function of the WBRF method.
+ */
+ for (i = 0; i < obj->buffer.length && i < 8; i++)
+ mask |= (((u64)obj->buffer.pointer[i]) << (i * 8));
The parens in the above expression are all redundant AFAICS.
And why does this need to be so complicated? There's only one caller
that passes only one bit in funcs, so why is this processing needed at
all? It looks like it would be better to fold this into its caller.
+}
+
+bool acpi_amd_wbrf_supported_consumer(struct device *dev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+
+ if (!adev)
+ return false;
+
+ return check_acpi_wbrf(adev->handle,
+ WBRF_REVISION,
+ BIT(WBRF_RETRIEVE));
+}
+
+int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
+ struct wbrf_ranges_out *out)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+ struct amd_wbrf_ranges_out acpi_out = {0};
+ union acpi_object *obj;
+
+ if (!adev)
+ return -ENODEV;
+
+ obj = acpi_evaluate_wbrf(adev->handle,
+ WBRF_REVISION,
+ WBRF_RETRIEVE);
+ if (!obj)
+ return -EINVAL;
+
+ /*
+ * The return buffer is with variable length and the format below:
+ * number_of_entries(1 DWORD): Number of entries
+ * start_freq of 1st entry(1 QWORD): Start frequency of the 1st entry
+ * end_freq of 1st entry(1 QWORD): End frequency of the 1st entry
+ * ...
+ * ...
+ * start_freq of the last entry(1 QWORD)
+ * end_freq of the last entry(1 QWORD)
+ *
+ * Thus the buffer length is determined by the number of entries.
+ * - For zero entry scenario, the buffer length will be 4 bytes.
+ * - For one entry scenario, the buffer length will be 20 bytes.
+ */
+ if (obj->buffer.length > sizeof(acpi_out) ||
+ obj->buffer.length < 4) {
+ dev_err(dev, "BIOS FUBAR, ignoring wrong sized WBRT information");
What does FUBAR mean here?
Why is it printed with dev_err()?
+ ACPI_FREE(obj);
+ return -EINVAL;
This can jump to a label instead of doing a duplicate ACPI_FREE(obj).
While using memcpy() here seems nicer, I would copy the list in a
proper loop item by item. Then it wouldn't be necessary to match the
sizes of elements of the source and destination arrays.
@@ -242,4 +242,24 @@ config FW_DEVLINK_SYNC_STATE_TIMEOUTcommandlineoptiononeverysystem/boardyourkernelisexpectedtoworkon.+menu"Wifi band RF mitigation mechanism"++configWBRF_AMD_ACPI+bool"ACPI based mechanism introduced by AMD"+depends onACPI+help+WifibandRFmitigationmechanismallowsmultipledriversfrom+differentdomainstonotifythefrequenciesinusesothathardware
s/notify/report/ I think.
+ can be reconfigured to avoid harmonic conflicts.
+
+ AMD has introduced an ACPI based mechanism to support WBRF for some
+ platforms with AMD dGPU and WLAN. This needs support from BIOS equipped
+ with necessary AML implementations and dGPU firmwares.
+
+ Before enabling this ACPI based mechanism, it is suggested to confirm
+ with the hardware designer/provider first whether your platform
+ equipped with necessary BIOS and firmwares.
The above is completely unworkable for distro kernel providers who
release one binary kernel that needs to work on all platforms.
@@ -30,11 +46,6 @@ static int __init parse_wbrf_policy_mode(char *p)}early_param("wbrf",parse_wbrf_policy_mode);-staticstructexclusion_range_pool{-structexclusion_rangeband_list[MAX_NUM_OF_WBRF_RANGES];-u64ref_counter[MAX_NUM_OF_WBRF_RANGES];-}wbrf_pool;-
So the previous patch should not add this definition here, should it?
quoted hunk
static int _wbrf_add_exclusion_ranges(struct wbrf_ranges_in *in)
{
int i, j;
@@ -121,20 +132,45 @@ static int _wbrf_retrieve_exclusion_ranges(struct wbrf_ranges_out *out) * * WBRF is used to mitigate devices that cause harmonic interference. * This function will determine if the platform is able to support the- * WBRF features.+ * WBRF features. For example, for AMD ACPI implementation it should say+ * true only when the necessary AML code/logic supporting wbrf feature+ * available.
The additional kerneldoc text doesn't seem to be particularly useful to me.
@@ -144,13 +180,22 @@ static bool wbrf_supported_system(void) * * WBRF is used to mitigate devices that cause harmonic interference. * This function will determine if this device should report such frequencies.+ * For example, for AMD ACPI implementation it should say true only when the+ * necessary AML code/logic supporting wbrf feature available for this device.
Again, the usefulness of the added kerneldoc text is quite questionable IMV.
*/
bool wbrf_supported_producer(struct device *dev)
{
- if (!wbrf_supported_system())
+ switch (wbrf_supported_system()) {
+ case WBRF_SUPPORT_GENERIC:
+ return true;
+ case WBRF_SUPPORT_OTHERS:
+#if IS_ENABLED(CONFIG_WBRF_AMD_ACPI)
@@ -166,11 +211,22 @@ EXPORT_SYMBOL_GPL(wbrf_supported_producer); int wbrf_add_exclusion(struct device *dev, struct wbrf_ranges_in *in) {- int r;+ int r = -ENODEV; mutex_lock(&wbrf_mutex);- r = _wbrf_add_exclusion_ranges(in);+ switch (wbrf_supported_system()) {+ case WBRF_SUPPORT_OTHERS:+#if IS_ENABLED(CONFIG_WBRF_AMD_ACPI)
Same here.
quoted hunk
+ r = acpi_amd_wbrf_add_exclusion(dev, in);
+#endif
+ break;
+ case WBRF_SUPPORT_GENERIC:
+ r = _wbrf_add_exclusion_ranges(in);
+ break;
+ default:
+ break;
+ }
mutex_unlock(&wbrf_mutex);
if (r)
@@ -194,11 +250,22 @@ EXPORT_SYMBOL_GPL(wbrf_add_exclusion); int wbrf_remove_exclusion(struct device *dev, struct wbrf_ranges_in *in) {- int r;+ int r = -ENODEV; mutex_lock(&wbrf_mutex);- r = _wbrf_remove_exclusion_ranges(in);+ switch (wbrf_supported_system()) {+ case WBRF_SUPPORT_OTHERS:+#if IS_ENABLED(CONFIG_WBRF_AMD_ACPI)
Same here.
quoted hunk
+ r = acpi_amd_wbrf_remove_exclusion(dev, in);
+#endif
+ break;
+ case WBRF_SUPPORT_GENERIC:
+ r = _wbrf_remove_exclusion_ranges(in);
+ break;
+ default:
+ break;
+ }
mutex_unlock(&wbrf_mutex);
if (r)
@@ -217,14 +284,23 @@ EXPORT_SYMBOL_GPL(wbrf_remove_exclusion); * * WBRF is used to mitigate devices that cause harmonic interference. * This function will determine if this device should react to reports from- * other devices for such frequencies.+ * other devices for such frequencies. For example, for AMD ACPI implementation+ * it should say true only when the necessary AML code/logic supporting wbrf+ * feature available for this device. */ bool wbrf_supported_consumer(struct device *dev) {- if (!wbrf_supported_system())+ switch (wbrf_supported_system()) {+ case WBRF_SUPPORT_GENERIC:+ return true;+ case WBRF_SUPPORT_OTHERS:+#if IS_ENABLED(CONFIG_WBRF_AMD_ACPI)
@@ -267,11 +343,22 @@ EXPORT_SYMBOL_GPL(wbrf_unregister_notifier); int wbrf_retrieve_exclusions(struct device *dev, struct wbrf_ranges_out *out) {- int r;+ int r = -ENODEV; mutex_lock(&wbrf_mutex);- r = _wbrf_retrieve_exclusion_ranges(out);+ switch (wbrf_supported_system()) {+ case WBRF_SUPPORT_OTHERS:+#if IS_ENABLED(CONFIG_WBRF_AMD_ACPI)
Same here.
quoted hunk
+ r = acpi_amd_wbrf_retrieve_exclusions(dev, out);
+#endif
+ break;
+ case WBRF_SUPPORT_GENERIC:
+ r = _wbrf_retrieve_exclusion_ranges(out);
+ break;
+ default:
+ break;
+ }
mutex_unlock(&wbrf_mutex);
Why is a wifi-specific thing going into drivers/base/?
confused,
greg k-h
The original problem statement was at a high level 'there can be
interference between different devices operating at high frequencies'.
The original patches introduced some ACPI library code that enabled a
mitigated for this interference between mac80211 devices and amdgpu devices.
Andrew Lunn wanted to see something more generic, so the series has
morphed into base code for things to advertise frequencies in use and
other things to listen to frequencies in use and react.
The idea is supposed to be that if the platform knows that these
mitigations are needed then the producers send the frequencies in use,
consumers react to them. The AMD implementation of getting this info
from the platform plugs into the base code (patch 2).
If users don't want this behavior they can turn it off on kernel command
line.
If the platform doesn't know mitigations are needed but user wants to
turn them on anyway they can turn it on kernel command line.
Why is a wifi-specific thing going into drivers/base/?
confused,
greg k-h
The original problem statement was at a high level 'there can be
interference between different devices operating at high frequencies'. The
original patches introduced some ACPI library code that enabled a mitigated
for this interference between mac80211 devices and amdgpu devices.
Andrew Lunn wanted to see something more generic, so the series has morphed
into base code for things to advertise frequencies in use and other things
to listen to frequencies in use and react.
The idea is supposed to be that if the platform knows that these mitigations
are needed then the producers send the frequencies in use, consumers react
to them. The AMD implementation of getting this info from the platform
plugs into the base code (patch 2).
If users don't want this behavior they can turn it off on kernel command
line.
If the platform doesn't know mitigations are needed but user wants to turn
them on anyway they can turn it on kernel command line.
That's all fine, I don't object to that at all. But bus/device-specific
stuff should NOT be in drivers/base/ if at all possible (yes, we do have
some exceptions with hypervisor.c and memory and cpu stuff) but for a
frequency thing like this, why can't it live with the other
wifi/frequency code in drivers/net/wireless/?
In other words, what's the benefit to having me be the maintainer of
this, someone who knows nothing about this subsystem, other than you
passing off that work to me? :)
thanks,
greg k-h
From: Johannes Berg <johannes@sipsolutions.net> Date: 2023-08-21 09:44:50
On Fri, 2023-08-18 at 11:26 +0800, Evan Quan wrote:
To support the WBRF mechanism, Wifi adapters utilized in the system must
register the frequencies in use(or unregister those frequencies no longer
used) via the dedicated calls. So that, other drivers responding to the
frequencies can take proper actions to mitigate possible interference.
Co-developed-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Co-developed-by: Evan Quan <redacted>
Signed-off-by: Evan Quan <redacted>
This has to setup ranges_in->num_of_ranges, no?
(Also no real good reason for num_of_ranges to be a u64, btw, since it
can only go up to 11)
With that fixed, you can add
Reviewed-by: Johannes Berg <johannes@sipsolutions.net>
johannes
Why is a wifi-specific thing going into drivers/base/?
confused,
greg k-h
The original problem statement was at a high level 'there can be
interference between different devices operating at high frequencies'. The
original patches introduced some ACPI library code that enabled a mitigated
for this interference between mac80211 devices and amdgpu devices.
Andrew Lunn wanted to see something more generic, so the series has morphed
into base code for things to advertise frequencies in use and other things
to listen to frequencies in use and react.
The idea is supposed to be that if the platform knows that these mitigations
are needed then the producers send the frequencies in use, consumers react
to them. The AMD implementation of getting this info from the platform
plugs into the base code (patch 2).
If users don't want this behavior they can turn it off on kernel command
line.
If the platform doesn't know mitigations are needed but user wants to turn
them on anyway they can turn it on kernel command line.
That's all fine, I don't object to that at all. But bus/device-specific
stuff should NOT be in drivers/base/ if at all possible (yes, we do have
some exceptions with hypervisor.c and memory and cpu stuff) but for a
frequency thing like this, why can't it live with the other
wifi/frequency code in drivers/net/wireless/?
In other words, what's the benefit to having me be the maintainer of
this, someone who knows nothing about this subsystem, other than you
passing off that work to me? :)
thanks,
greg k-h
The reason drivers/base was proposed was because although the initial
implementation is for producers from mac80211, Andrew pointed out that
many other things can technically be producers and cause interference
if not properly shielded.
So by making it part of base that sets up the policy that if something
"can" produce certain problematic harmonics that it can participate.
Whether or not other devices /will/ use this is another question though.
You need deep platform knowledge and proper equipment to diagnose a
problem and conclude it can be helped with this kind of mitigation.
So I wonder if the right answer is to put it in drivers/net/wireless
initially and if we come up with a need later for non wifi producers we
can discuss moving it at that time.
On Mon, Aug 21, 2023 at 10:13:45PM -0500, Limonciello, Mario wrote:
So I wonder if the right answer is to put it in drivers/net/wireless
initially and if we come up with a need later for non wifi producers we can
discuss moving it at that time.
-----Original Message-----
From: Johannes Berg <johannes@sipsolutions.net>
Sent: Monday, August 21, 2023 5:44 PM
To: Quan, Evan <redacted>; gregkh@linuxfoundation.org;
rafael@kernel.org; lenb@kernel.org; davem@davemloft.net;
edumazet@google.com; kuba@kernel.org; pabeni@redhat.com; Deucher,
Alexander [off-list ref]; andrew@lunn.ch;
rdunlap@infradead.org; quic_jjohnson@quicinc.com; horms@kernel.org
Cc: linux-doc@vger.kernel.org; linux-kernel@vger.kernel.org; linux-
acpi@vger.kernel.org; amd-gfx@lists.freedesktop.org; linux-
wireless@vger.kernel.org; netdev@vger.kernel.org; Limonciello, Mario
[off-list ref]
Subject: Re: [V9 4/9] wifi: mac80211: Add support for WBRF features
On Fri, 2023-08-18 at 11:26 +0800, Evan Quan wrote:
quoted
To support the WBRF mechanism, Wifi adapters utilized in the system
must register the frequencies in use(or unregister those frequencies
no longer
used) via the dedicated calls. So that, other drivers responding to
the frequencies can take proper actions to mitigate possible interference.
Co-developed-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Co-developed-by: Evan Quan <redacted>
Signed-off-by: Evan Quan <redacted>
(Also no real good reason for num_of_ranges to be a u64, btw, since it can
only go up to 11)
Mainly for data structure alignment. Since other members come with u64.
So, to make the data structure naturally aligned, 'num_of_ranges' also comes with u64.
Evan
With that fixed, you can add
Reviewed-by: Johannes Berg <johannes@sipsolutions.net>
johannes
-----Original Message-----
From: Lazar, Lijo <redacted>
Sent: Friday, August 18, 2023 5:12 PM
To: Quan, Evan <redacted>; gregkh@linuxfoundation.org;
rafael@kernel.org; lenb@kernel.org; johannes@sipsolutions.net;
davem@davemloft.net; edumazet@google.com; kuba@kernel.org;
pabeni@redhat.com; Deucher, Alexander [off-list ref];
andrew@lunn.ch; rdunlap@infradead.org; quic_jjohnson@quicinc.com;
horms@kernel.org
Cc: linux-doc@vger.kernel.org; netdev@vger.kernel.org; linux-
wireless@vger.kernel.org; linux-kernel@vger.kernel.org; amd-
gfx@lists.freedesktop.org; linux-acpi@vger.kernel.org; Limonciello, Mario
[off-list ref]
Subject: Re: [V9 6/9] drm/amd/pm: setup the framework to support Wifi RFI
mitigation feature
On 8/18/2023 8:56 AM, Evan Quan wrote:
quoted
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).
Signed-off-by: Evan Quan <redacted>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
--
v1->v2:
- update the prompt for feature support(Lijo)
v8->v9:
- update parameter document for smu_wbrf_event_handler(Simon)
---
drivers/gpu/drm/amd/amdgpu/amdgpu.h | 2 +
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 17 ++
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c | 194
module_param(enforce_isolation, bool, 0444);
MODULE_PARM_DESC(enforce_isolation, "enforce process isolation
between graphics and compute . enforce_isolation = on");
+/**
+ * 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
*/
If continued at this point, it won't eliminate all duplicates.
Example:
(2000,2100)(0,0)(1100,1200)(0, 0)(2500,2600)(3000,3200)(1100,1200)
Once it places (1100,1200) at index 1, it will continue the loop and then it
skips duplicate entry at index 2.
Thanks for pointing this out. Fixed in V10.
Evan
Once replaced, you may assign the new start/end
start = wifi_bands[i].start = wifi_bands[j].start;
end= wifi_bands[i].end = wifi_bands[j].end;
and then proceed to the duplicate entries scan loop below.
I think this'continue' is not required.
Thanks,
Lijo
quoted
+ }
+ }
+ }
+
+ /* Send the sorted wifi_bands to PMFW */
+ ret = smu_set_wbrf_exclusion_ranges(smu, wifi_bands);
+ /* Give it another chance */
+ 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 &&
+ wbrf_supported_consumer(adev->dev);
+
+ if (smu->wbrf_supported)
+ dev_info(adev->dev, "RF interference mitigation is
supported\n"); }
quoted
+
+/**
+ * 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 = 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");
quoted
+
+ 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;
+
+ wbrf_unregister_notifier(&smu->wbrf_notifier);
+}
+
static int smu_smc_hw_setup(struct smu_context *smu)
{
struct smu_feature *feature = &smu->smu_feature; @@ -1320,6
+1487,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");
quoted
+ return ret;
+ }
+ }
+
/*
* With SCPM enabled, these actions(and relevant messages) are
* not needed and permitted.
@@ -1416,6 +1592,15 @@ static int smu_smc_hw_setup(struct
smu_context *smu)
quoted
*/
ret = smu_set_min_dcef_deep_sleep(smu,
smu->smu_table.boot_values.dcefclk
/ 100);
quoted
+ 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;
}
@@ -1471,6 +1656,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)
@@ -1623,6 +1815,8 @@ static int smu_smc_hw_cleanup(struct
smu_context *smu)
quoted
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);
@@ -575,6 +577,10 @@ struct smu_contextu32debug_resp_reg;structdelayed_workswctf_delayed_work;++/* data structures for wbrf feature support */+boolwbrf_supported;+structnotifier_blockwbrf_notifier;};structi2c_adapter;