2023-06-09 07:31:30

by Evan Quan

[permalink] [raw]
Subject: [PATCH V2 0/7] Support Wifi RFI interference mitigation feature

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 6.4-rc3. With some brief introductions as below:
Patch1: Core ACPI interfaces needed to support WBRF feature.
Patch2: Enable WBRF support for wifi subsystem.
Patch3 - 7: Enable WBRF support for AMD graphics driver.

Evan Quan (5):
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

Mario Limonciello (2):
drivers/acpi: Add support for Wifi band RF mitigations
wifi: mac80211: Add support for ACPI WBRF

drivers/acpi/Kconfig | 7 +
drivers/acpi/Makefile | 2 +
drivers/acpi/acpi_wbrf.c | 215 ++++++++++++++++++
drivers/gpu/drm/amd/amdgpu/amdgpu.h | 26 +++
drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c | 63 +++++
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 19 ++
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c | 204 +++++++++++++++++
drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h | 30 +++
.../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/wbrf.h | 55 +++++
include/net/cfg80211.h | 18 ++
net/mac80211/Makefile | 2 +
net/mac80211/chan.c | 6 +
net/mac80211/main.c | 2 +
net/mac80211/wbrf.c | 183 +++++++++++++++
24 files changed, 998 insertions(+), 5 deletions(-)
create mode 100644 drivers/acpi/acpi_wbrf.c
create mode 100644 include/linux/wbrf.h
create mode 100644 net/mac80211/wbrf.c

--
2.34.1



2023-06-09 07:31:30

by Evan Quan

[permalink] [raw]
Subject: [PATCH V2 6/7] drm/amd/pm: enable Wifi RFI mitigation feature support for SMU13.0.0

Fulfill the SMU13.0.0 support for Wifi RFI mitigation feature.

Signed-off-by: Evan Quan <[email protected]>
---
drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.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 +++++++++++++++++++
5 files changed, 77 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
index aa63cc43d41c..a8a4be32cc59 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@ -323,6 +323,7 @@ enum smu_table_id
SMU_TABLE_PACE,
SMU_TABLE_ECCINFO,
SMU_TABLE_COMBO_PPTABLE,
+ SMU_TABLE_WIFIBAND,
SMU_TABLE_COUNT,
};

@@ -1496,6 +1497,8 @@ enum smu_baco_seq {
__dst_size); \
})

+#define HZ_IN_MHZ 1000000U
+
#if !defined(SWSMU_CODE_LAYER_L2) && !defined(SWSMU_CODE_LAYER_L3) && !defined(SWSMU_CODE_LAYER_L4)
int smu_get_power_limit(void *handle,
uint32_t *limit,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h
index 297b70b9388f..5bbb60289a79 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h
@@ -245,7 +245,8 @@
__SMU_DUMMY_MAP(AllowGpo), \
__SMU_DUMMY_MAP(Mode2Reset), \
__SMU_DUMMY_MAP(RequestI2cTransaction), \
- __SMU_DUMMY_MAP(GetMetricsTable),
+ __SMU_DUMMY_MAP(GetMetricsTable), \
+ __SMU_DUMMY_MAP(EnableUCLKShadow),

#undef __SMU_DUMMY_MAP
#define __SMU_DUMMY_MAP(type) SMU_MSG_##type
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h
index df3baaab0037..b6fae9b92303 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h
@@ -303,5 +303,8 @@ int smu_v13_0_get_pptable_from_firmware(struct smu_context *smu,
uint32_t *size,
uint32_t pptable_id);

+int smu_v13_0_enable_uclk_shadow(struct smu_context *smu,
+ bool enablement);
+
#endif
#endif
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c
index 393c6a7b9609..8c2230d1d862 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c
@@ -2453,3 +2453,12 @@ int smu_v13_0_mode1_reset(struct smu_context *smu)

return ret;
}
+
+int smu_v13_0_enable_uclk_shadow(struct smu_context *smu,
+ bool enablement)
+{
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_EnableUCLKShadow,
+ enablement,
+ NULL);
+}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
index 09405ef1e3c8..46000039e511 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
@@ -155,6 +155,7 @@ static struct cmn2asic_msg_mapping smu_v13_0_0_message_map[SMU_MSG_MAX_COUNT] =
MSG_MAP(AllowGpo, PPSMC_MSG_SetGpoAllow, 0),
MSG_MAP(AllowIHHostInterrupt, PPSMC_MSG_AllowIHHostInterrupt, 0),
MSG_MAP(ReenableAcDcInterrupt, PPSMC_MSG_ReenableAcDcInterrupt, 0),
+ MSG_MAP(EnableUCLKShadow, PPSMC_MSG_EnableUCLKShadow, 0),
};

static struct cmn2asic_mapping smu_v13_0_0_clk_map[SMU_CLK_COUNT] = {
@@ -235,6 +236,7 @@ static struct cmn2asic_mapping smu_v13_0_0_table_map[SMU_TABLE_COUNT] = {
TAB_MAP(DRIVER_SMU_CONFIG),
TAB_MAP(ACTIVITY_MONITOR_COEFF),
[SMU_TABLE_COMBO_PPTABLE] = {1, TABLE_COMBO_PPTABLE},
+ TAB_MAP(WIFIBAND),
TAB_MAP(I2C_COMMANDS),
TAB_MAP(ECCINFO),
};
@@ -472,6 +474,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)
@@ -2112,6 +2117,58 @@ static ssize_t smu_v13_0_0_get_ecc_info(struct smu_context *smu,
return ret;
}

+static bool smu_v13_0_0_wbrf_support_check(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ switch (adev->ip_versions[MP1_HWIP][0]) {
+ /* PMFWs supporting WBRF feature are not yet available */
+ case IP_VERSION(13, 0, 0):
+ case IP_VERSION(13, 0, 10):
+ return false;
+ default:
+ return false;
+ }
+}
+
+static int smu_v13_0_0_set_wbrf_exclusion_ranges(struct smu_context *smu,
+ struct exclusion_range *exclusion_ranges)
+{
+ WifiBandEntryTable_t wifi_bands;
+ int valid_entries = 0;
+ int ret, 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;
+
+ /*
+ * Per confirm with PMFW team, WifiBandEntryNum = 0
+ * is a valid setting. So, there should be no direct
+ * return on that.
+ */
+
+ 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!");
+
+ return ret;
+}
+
static const struct pptable_funcs smu_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,
@@ -2188,6 +2245,9 @@ static const struct pptable_funcs smu_v13_0_0_ppt_funcs = {
.send_hbm_bad_channel_flag = smu_v13_0_0_send_bad_mem_channel_flag,
.gpo_control = smu_v13_0_gpo_control,
.get_ecc_info = smu_v13_0_0_get_ecc_info,
+ .is_asic_wbrf_supported = smu_v13_0_0_wbrf_support_check,
+ .enable_uclk_shadow = smu_v13_0_enable_uclk_shadow,
+ .set_wbrf_exclusion_ranges = smu_v13_0_0_set_wbrf_exclusion_ranges,
};

void smu_v13_0_0_set_ppt_funcs(struct smu_context *smu)
--
2.34.1


2023-06-09 07:31:30

by Evan Quan

[permalink] [raw]
Subject: [PATCH V2 1/7] drivers/acpi: Add support for Wifi band RF mitigations

From: Mario Limonciello <[email protected]>

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 an ACPI based mechanism that
devices can use to notify active use of particular frequencies so
that 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_retrieve_exclusions` to retrieve the current
exclusions on receiving an ACPI notification for a new frequency
change.

Signed-off-by: Mario Limonciello <[email protected]>
Co-developed-by: Evan Quan <[email protected]>
Signed-off-by: Evan Quan <[email protected]>
--
v1->v2:
- move those wlan specific implementations to net/mac80211(Mario)
---
drivers/acpi/Kconfig | 7 ++
drivers/acpi/Makefile | 2 +
drivers/acpi/acpi_wbrf.c | 215 +++++++++++++++++++++++++++++++++++++++
include/linux/wbrf.h | 55 ++++++++++
4 files changed, 279 insertions(+)
create mode 100644 drivers/acpi/acpi_wbrf.c
create mode 100644 include/linux/wbrf.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index ccbeab9500ec..9ee7c7dcc3e6 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -611,3 +611,10 @@ config X86_PM_TIMER

You should nearly always say Y here because many modern
systems require this timer.
+
+config ACPI_WBRF
+ bool "ACPI Wifi band RF mitigation mechanism"
+ help
+ Wifi band RF mitigation mechanism allows multiple drivers from
+ different domains to notify the frequencies in use so that hardware
+ can be reconfigured to avoid harmonic conflicts.
\ No newline at end of file
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index feb36c0b9446..be173e76aa62 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -131,3 +131,5 @@ obj-y += dptf/
obj-$(CONFIG_ARM64) += arm64/

obj-$(CONFIG_ACPI_VIOT) += viot.o
+
+obj-$(CONFIG_ACPI_WBRF) += acpi_wbrf.o
\ No newline at end of file
diff --git a/drivers/acpi/acpi_wbrf.c b/drivers/acpi/acpi_wbrf.c
new file mode 100644
index 000000000000..8c275998ac29
--- /dev/null
+++ b/drivers/acpi/acpi_wbrf.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Wifi Band Exclusion Interface
+ * Copyright (C) 2023 Advanced Micro Devices
+ *
+ */
+
+#include <linux/wbrf.h>
+
+/* functions */
+#define WBRF_RECORD 0x1
+#define WBRF_RETRIEVE 0x2
+
+/* record actions */
+#define WBRF_RECORD_ADD 0x0
+#define WBRF_RECORD_REMOVE 0x1
+
+#define WBRF_REVISION 0x1
+
+static const guid_t wifi_acpi_dsm_guid =
+ GUID_INIT(0x7b7656cf, 0xdc3d, 0x4c1c,
+ 0x83, 0xe9, 0x66, 0xe7, 0x21, 0xde, 0x30, 0x70);
+
+static int wbrf_dsm(struct acpi_device *adev, u8 fn,
+ union acpi_object *argv4,
+ union acpi_object **out)
+{
+ union acpi_object *obj;
+ int rc;
+
+ obj = acpi_evaluate_dsm(adev->handle, &wifi_acpi_dsm_guid,
+ WBRF_REVISION, fn, argv4);
+ if (!obj)
+ return -ENXIO;
+
+ switch (obj->type) {
+ case ACPI_TYPE_BUFFER:
+ if (!*out) {
+ rc = -EINVAL;
+ break;
+ }
+ *out = obj;
+ return 0;
+
+ case ACPI_TYPE_INTEGER:
+ rc = obj->integer.value ? -EINVAL : 0;
+ break;
+ default:
+ rc = -EOPNOTSUPP;
+ }
+ ACPI_FREE(obj);
+
+ return rc;
+}
+
+static int wbrf_record(struct acpi_device *adev, uint8_t action,
+ struct wbrf_ranges_in *in)
+{
+ union acpi_object *argv4;
+ uint32_t num_of_ranges = 0;
+ uint32_t arg_idx = 0;
+ uint32_t 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++;
+
+ argv4 = kzalloc(sizeof(*argv4) * (2 * num_of_ranges + 2 + 1), GFP_KERNEL);
+ if (!argv4)
+ return -ENOMEM;
+
+ argv4[arg_idx].package.type = ACPI_TYPE_PACKAGE;
+ argv4[arg_idx].package.count = 2 + 2 * num_of_ranges;
+ argv4[arg_idx++].package.elements = &argv4[1];
+ argv4[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+ argv4[arg_idx++].integer.value = num_of_ranges;
+ argv4[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+ argv4[arg_idx++].integer.value = action;
+
+ 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)
+ continue;
+
+ argv4[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+ argv4[arg_idx++].integer.value = in->band_list[loop_idx].start;
+ argv4[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+ argv4[arg_idx++].integer.value = in->band_list[loop_idx].end;
+ }
+
+ ret = wbrf_dsm(adev, WBRF_RECORD, argv4, NULL);
+
+ kfree(argv4);
+
+ return ret;
+}
+
+int wbrf_add_exclusion(struct acpi_device *adev,
+ struct wbrf_ranges_in *in)
+{
+ return wbrf_record(adev, WBRF_RECORD_ADD, in);
+}
+EXPORT_SYMBOL_GPL(wbrf_add_exclusion);
+
+int wbrf_remove_exclusion(struct acpi_device *adev,
+ struct wbrf_ranges_in *in)
+{
+ return wbrf_record(adev, WBRF_RECORD_REMOVE, in);
+}
+EXPORT_SYMBOL_GPL(wbrf_remove_exclusion);
+
+bool wbrf_supported_producer(struct acpi_device *adev)
+{
+ return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid,
+ WBRF_REVISION,
+ (1ULL << WBRF_RECORD) | (1ULL << WBRF_RETRIEVE));
+}
+EXPORT_SYMBOL_GPL(wbrf_supported_producer);
+
+static union acpi_object *
+acpi_evaluate_wbrf(acpi_handle handle, u64 rev, u64 func)
+{
+ acpi_status ret;
+ struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object params[4];
+ struct acpi_object_list input = {
+ .count = 4,
+ .pointer = params,
+ };
+
+ params[0].type = ACPI_TYPE_INTEGER;
+ params[0].integer.value = rev;
+ params[1].type = ACPI_TYPE_INTEGER;
+ params[1].integer.value = func;
+ params[2].type = ACPI_TYPE_PACKAGE;
+ params[2].package.count = 0;
+ params[2].package.elements = NULL;
+ params[3].type = ACPI_TYPE_STRING;
+ params[3].string.length = 0;
+ params[3].string.pointer= NULL;
+
+ ret = acpi_evaluate_object(handle, "WBRF", &input, &buf);
+ if (ACPI_SUCCESS(ret))
+ return (union acpi_object *)buf.pointer;
+
+ if (ret != AE_NOT_FOUND)
+ acpi_handle_warn(handle,
+ "failed to evaluate WBRF(0x%x)\n", ret);
+
+ return NULL;
+}
+
+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;
+
+ for (i = 0; i < obj->buffer.length && i < 8; i++)
+ mask |= (((u64)obj->buffer.pointer[i]) << (i * 8));
+ ACPI_FREE(obj);
+
+ /*
+ * Bit 0 indicates whether there's support for any functions other than
+ * function 0.
+ */
+ if ((mask & 0x1) && (mask & funcs) == funcs)
+ return true;
+
+ return false;
+}
+
+bool wbrf_supported_consumer(struct acpi_device *adev)
+{
+ return check_acpi_wbrf(adev->handle,
+ WBRF_REVISION,
+ 1ULL << WBRF_RETRIEVE);
+}
+EXPORT_SYMBOL_GPL(wbrf_supported_consumer);
+
+int wbrf_retrieve_exclusions(struct acpi_device *adev,
+ struct wbrf_ranges_out *exclusions_out)
+{
+ union acpi_object *obj;
+
+ obj = acpi_evaluate_wbrf(adev->handle,
+ WBRF_REVISION,
+ WBRF_RETRIEVE);
+ if (!obj)
+ return -EINVAL;
+
+ memcpy(exclusions_out, obj->buffer.pointer, obj->buffer.length);
+
+ ACPI_FREE(obj);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wbrf_retrieve_exclusions);
diff --git a/include/linux/wbrf.h b/include/linux/wbrf.h
new file mode 100644
index 000000000000..e4c99b69f1d2
--- /dev/null
+++ b/include/linux/wbrf.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * AMD Wifi Band Exclusion Interface
+ * Copyright (C) 2023 Advanced Micro Devices
+ */
+
+#ifndef _LINUX_WBRF_H
+#define _LINUX_WBRF_H
+
+#include <linux/acpi.h>
+
+/* Maximum number of wbrf ranges */
+#define MAX_NUM_OF_WBRF_RANGES 11
+
+struct exclusion_range {
+ /* start and end point of the frequency range in Hz */
+ uint64_t start;
+ uint64_t end;
+};
+
+struct wbrf_ranges_in {
+ /* valid entry: `start` and `end` filled with non-zero values */
+ struct exclusion_range band_list[MAX_NUM_OF_WBRF_RANGES];
+};
+
+struct wbrf_ranges_out {
+ uint32_t num_of_ranges;
+ struct exclusion_range band_list[MAX_NUM_OF_WBRF_RANGES];
+} __attribute__((packed));
+
+/**
+ * APIs needed by drivers/subsystems for contributing frequencies:
+ * During probe, check `wbrf_supported_producer` to see if WBRF is supported.
+ * If adding frequencies, then call `wbrf_add_exclusion` with the
+ * start and end points specified for the frequency ranges added.
+ * If removing frequencies, then call `wbrf_remove_exclusion` with
+ * start and end points specified for the frequency ranges added.
+ */
+bool wbrf_supported_producer(struct acpi_device *adev);
+int wbrf_add_exclusion(struct acpi_device *adev,
+ struct wbrf_ranges_in *in);
+int wbrf_remove_exclusion(struct acpi_device *adev,
+ struct wbrf_ranges_in *in);
+
+/**
+ * APIs needed by drivers/subsystems responding to frequencies:
+ * During probe, check `wbrf_supported_consumer` to see if WBRF is supported.
+ * When receiving an ACPI notification for some frequencies change, run
+ * `wbrf_retrieve_exclusions` to retrieve the latest frequencies ranges.
+ */
+int wbrf_retrieve_exclusions(struct acpi_device *adev,
+ struct wbrf_ranges_out *out);
+bool wbrf_supported_consumer(struct acpi_device *adev);
+
+#endif /* _LINUX_WBRF_H */
--
2.34.1


2023-06-09 07:31:40

by Evan Quan

[permalink] [raw]
Subject: [PATCH V2 4/7] 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).

To make WBRF feature functional, the kernel needs to be configured with
CONFIG_ACPI_WBRF and the platform is equipped with necessary ACPI based
mechanism to get amdgpu driver notified.

Signed-off-by: Evan Quan <[email protected]>
---
drivers/gpu/drm/amd/amdgpu/amdgpu.h | 26 +++
drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c | 63 ++++++
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 19 ++
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c | 184 ++++++++++++++++++
drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h | 20 ++
drivers/gpu/drm/amd/pm/swsmu/smu_internal.h | 3 +
6 files changed, 315 insertions(+)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 02b827785e39..2f2ec64ed1b2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -50,6 +50,7 @@
#include <linux/hashtable.h>
#include <linux/dma-fence.h>
#include <linux/pci.h>
+#include <linux/wbrf.h>

#include <drm/ttm/ttm_bo.h>
#include <drm/ttm/ttm_placement.h>
@@ -241,6 +242,7 @@ extern int amdgpu_num_kcq;
#define AMDGPU_VCNFW_LOG_SIZE (32 * 1024)
extern int amdgpu_vcnfw_log;
extern int amdgpu_sg_display;
+extern int amdgpu_wbrf;

#define AMDGPU_VM_MAX_NUM_CTX 4096
#define AMDGPU_SG_THRESHOLD (256*1024*1024)
@@ -741,6 +743,9 @@ struct amdgpu_reset_domain;
*/
#define AMDGPU_HAS_VRAM(_adev) ((_adev)->gmc.real_vram_size)

+typedef
+void (*wbrf_notify_handler) (struct amdgpu_device *adev);
+
struct amdgpu_device {
struct device *dev;
struct pci_dev *pdev;
@@ -1050,6 +1055,8 @@ struct amdgpu_device {

bool job_hang;
bool dc_enabled;
+
+ wbrf_notify_handler wbrf_event_handler;
};

static inline struct amdgpu_device *drm_to_adev(struct drm_device *ddev)
@@ -1381,6 +1388,25 @@ static inline int amdgpu_acpi_smart_shift_update(struct drm_device *dev,
enum amdgpu_ss ss_state) { return 0; }
#endif

+#if defined(CONFIG_ACPI_WBRF)
+bool amdgpu_acpi_is_wbrf_supported(struct amdgpu_device *adev);
+int amdgpu_acpi_wbrf_retrieve_exclusions(struct amdgpu_device *adev,
+ struct wbrf_ranges_out *exclusions_out);
+int amdgpu_acpi_register_wbrf_notify_handler(struct amdgpu_device *adev,
+ wbrf_notify_handler handler);
+int amdgpu_acpi_unregister_wbrf_notify_handler(struct amdgpu_device *adev);
+#else
+static inline bool amdgpu_acpi_is_wbrf_supported(struct amdgpu_device *adev) { return false; }
+static inline
+int amdgpu_acpi_wbrf_retrieve_exclusions(struct amdgpu_device *adev,
+ struct wbrf_ranges_out *exclusions_out) { return 0; }
+static inline
+int amdgpu_acpi_register_wbrf_notify_handler(struct amdgpu_device *adev,
+ wbrf_notify_handler handler) { return 0; }
+static inline
+int amdgpu_acpi_unregister_wbrf_notify_handler(struct amdgpu_device *adev) { return 0; }
+#endif
+
#if defined(CONFIG_ACPI) && defined(CONFIG_SUSPEND)
bool amdgpu_acpi_is_s3_active(struct amdgpu_device *adev);
bool amdgpu_acpi_is_s0ix_active(struct amdgpu_device *adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
index aeeec211861c..efbe6dd91d1a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
@@ -1105,3 +1105,66 @@ bool amdgpu_acpi_is_s0ix_active(struct amdgpu_device *adev)
}

#endif /* CONFIG_SUSPEND */
+
+#ifdef CONFIG_ACPI_WBRF
+bool amdgpu_acpi_is_wbrf_supported(struct amdgpu_device *adev)
+{
+ struct acpi_device *acpi_dev = ACPI_COMPANION(adev->dev);
+
+ if (!acpi_dev)
+ return false;
+
+ return wbrf_supported_consumer(acpi_dev);
+}
+
+int amdgpu_acpi_wbrf_retrieve_exclusions(struct amdgpu_device *adev,
+ struct wbrf_ranges_out *exclusions_out)
+{
+ struct acpi_device *acpi_dev = ACPI_COMPANION(adev->dev);
+
+ if (!acpi_dev)
+ return -ENODEV;
+
+ return wbrf_retrieve_exclusions(acpi_dev, exclusions_out);
+}
+
+#define CPM_GPU_NOTIFY_COMMAND 0x55
+static void amdgpu_acpi_wbrf_event(acpi_handle handle, u32 event, void *data)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)data;
+
+ if (event == CPM_GPU_NOTIFY_COMMAND &&
+ adev->wbrf_event_handler)
+ adev->wbrf_event_handler(adev);
+}
+
+int amdgpu_acpi_register_wbrf_notify_handler(struct amdgpu_device *adev,
+ wbrf_notify_handler handler)
+{
+ struct acpi_handle *acpi_hdler = ACPI_HANDLE(adev->dev);
+
+ if (!acpi_hdler)
+ return -ENODEV;
+
+ adev->wbrf_event_handler = handler;
+
+ return acpi_install_notify_handler(acpi_hdler,
+ ACPI_ALL_NOTIFY,
+ amdgpu_acpi_wbrf_event,
+ adev);
+}
+
+int amdgpu_acpi_unregister_wbrf_notify_handler(struct amdgpu_device *adev)
+{
+ struct acpi_handle *acpi_hdler = ACPI_HANDLE(adev->dev);
+
+ if (!acpi_hdler)
+ return -ENODEV;
+
+ adev->wbrf_event_handler = NULL;
+
+ return acpi_remove_notify_handler(acpi_hdler,
+ ACPI_ALL_NOTIFY,
+ amdgpu_acpi_wbrf_event);
+}
+#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index b1ca1ab6d6ad..bf82cc192153 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -191,6 +191,7 @@ int amdgpu_smartshift_bias;
int amdgpu_use_xgmi_p2p = 1;
int amdgpu_vcnfw_log;
int amdgpu_sg_display = -1; /* auto */
+int amdgpu_wbrf = -1;

static void amdgpu_drv_delayed_reset_work_handler(struct work_struct *work);

@@ -948,6 +949,24 @@ MODULE_PARM_DESC(smu_pptable_id,
"specify pptable id to be used (-1 = auto(default) value, 0 = use pptable from vbios, > 0 = soft pptable id)");
module_param_named(smu_pptable_id, amdgpu_smu_pptable_id, int, 0444);

+#ifdef CONFIG_ACPI_WBRF
+/**
+ * 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);
+#endif
+
/* These devices are not supported by amdgpu.
* They are supported by the mach64, r128, radeon drivers
*/
diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
index 2ddf5198e5c4..89f876cc60e6 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
@@ -1188,6 +1188,163 @@ 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_out wbrf_exclusion = {0};
+ struct exclusion_range *wifi_bands = wbrf_exclusion.band_list;
+ struct amdgpu_device *adev = smu->adev;
+ uint64_t start, end;
+ int ret, i, j;
+
+ ret = amdgpu_acpi_wbrf_retrieve_exclusions(adev, &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 < 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);
+ }
+
+ return ret;
+}
+
+/**
+ * smu_wbrf_event_handler - handle notify events
+ *
+ * @adev: struct amdgpu_device pointer
+ *
+ * Calls relevant amdgpu function in response to wbrf event
+ * notification from BIOS.
+ */
+static void smu_wbrf_event_handler(struct amdgpu_device *adev)
+{
+ struct smu_context *smu = adev->powerplay.pp_handle;
+
+ smu_wbrf_handle_exclusion_ranges(smu);
+}
+
+/**
+ * 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 &&
+ amdgpu_acpi_is_wbrf_supported(adev);
+
+ if (smu->wbrf_supported)
+ dev_info(adev->dev, "Enabled RF interference mitigations\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;
+
+ ret = amdgpu_acpi_register_wbrf_notify_handler(adev,
+ smu_wbrf_event_handler);
+ 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)
+{
+ struct amdgpu_device *adev = smu->adev;
+
+ if (!smu->wbrf_supported)
+ return;
+
+ amdgpu_acpi_unregister_wbrf_notify_handler(adev);
+}
+
static int smu_smc_hw_setup(struct smu_context *smu)
{
struct smu_feature *feature = &smu->smu_feature;
@@ -1280,6 +1437,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.
@@ -1376,6 +1542,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;
}
@@ -1431,6 +1606,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)
@@ -1583,6 +1765,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);

diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
index 09469c750a96..ff0af3da0be2 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@ -573,6 +573,9 @@ struct smu_context
u32 debug_param_reg;
u32 debug_msg_reg;
u32 debug_resp_reg;
+
+ /* data structures for wbrf feature support */
+ bool wbrf_supported;
};

struct i2c_adapter;
@@ -1354,6 +1357,23 @@ struct pptable_funcs {
* @init_pptable_microcode: Prepare the pptable microcode to upload via PSP
*/
int (*init_pptable_microcode)(struct smu_context *smu);
+
+ /**
+ * @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 enablement);
+
+ /**
+ * @set_wbrf_exclusion_ranges: notify SMU the wifi bands occupied
+ */
+ int (*set_wbrf_exclusion_ranges)(struct smu_context *smu,
+ struct exclusion_range *exclusion_ranges);
};

typedef enum {
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_internal.h b/drivers/gpu/drm/amd/pm/swsmu/smu_internal.h
index ceb13c838067..67d7495ab49e 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu_internal.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu_internal.h
@@ -97,6 +97,9 @@
#define smu_get_default_config_table_settings(smu, config_table) smu_ppt_funcs(get_default_config_table_settings, -EOPNOTSUPP, smu, config_table)
#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_is_asic_wbrf_supported(smu) smu_ppt_funcs(is_asic_wbrf_supported, false, smu)
+#define smu_enable_uclk_shadow(smu, enablement) smu_ppt_funcs(enable_uclk_shadow, 0, smu, enablement)
+#define smu_set_wbrf_exclusion_ranges(smu, exclusion_ranges) smu_ppt_funcs(set_wbrf_exclusion_ranges, -EOPNOTSUPP, smu, exclusion_ranges)

#endif
#endif
--
2.34.1


2023-06-09 07:31:49

by Evan Quan

[permalink] [raw]
Subject: [PATCH V2 2/7] wifi: mac80211: Add support for ACPI WBRF

From: Mario Limonciello <[email protected]>

To support AMD's WBRF interference mitigation mechanism, Wifi adapters
utilized in the system must register the frequencies in use(or unregister
those frequencies no longer used) via the dedicated APCI calls. So that,
other drivers responding to the frequencies can take proper actions to
mitigate possible interference.

To make WBRF feature functional, the kernel needs to be configured with
CONFIG_ACPI_WBRF and the platform is equipped with WBRF support(from
BIOS and drivers).

Signed-off-by: Mario Limonciello <[email protected]>
Co-developed-by: Evan Quan <[email protected]>
Signed-off-by: Evan Quan <[email protected]>
---
include/net/cfg80211.h | 18 ++++
net/mac80211/Makefile | 2 +
net/mac80211/chan.c | 6 ++
net/mac80211/main.c | 2 +
net/mac80211/wbrf.c | 183 +++++++++++++++++++++++++++++++++++++++++
5 files changed, 211 insertions(+)
create mode 100644 net/mac80211/wbrf.c

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 9e04f69712b1..d995ba085692 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -5551,6 +5551,10 @@ struct wiphy {

u16 hw_timestamp_max_peers;

+#ifdef CONFIG_ACPI_WBRF
+ bool wbrf_supported;
+#endif
+
char priv[] __aligned(NETDEV_ALIGN);
};

@@ -9067,4 +9071,18 @@ static inline int cfg80211_color_change_notify(struct net_device *dev)
bool cfg80211_valid_disable_subchannel_bitmap(u16 *bitmap,
const struct cfg80211_chan_def *chandef);

+#ifdef CONFIG_ACPI_WBRF
+void ieee80211_check_wbrf_support(struct wiphy *wiphy);
+int ieee80211_add_wbrf(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef);
+void ieee80211_remove_wbrf(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef);
+#else
+static inline void ieee80211_check_wbrf_support(struct wiphy *wiphy) { }
+static inline int ieee80211_add_wbrf(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef) { return 0; }
+static inline void ieee80211_remove_wbrf(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef) { }
+#endif /* CONFIG_ACPI_WBRF */
+
#endif /* __NET_CFG80211_H */
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index b8de44da1fb8..709eb678f42a 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -65,4 +65,6 @@ rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += \

mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y)

+mac80211-$(CONFIG_ACPI_WBRF) += wbrf.o
+
ccflags-y += -DDEBUG
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 77c90ed8f5d7..d26a3f622e50 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -668,6 +668,10 @@ static int ieee80211_add_chanctx(struct ieee80211_local *local,
lockdep_assert_held(&local->mtx);
lockdep_assert_held(&local->chanctx_mtx);

+ err = ieee80211_add_wbrf(local->hw.wiphy, &ctx->conf.def);
+ if (err)
+ return err;
+
if (!local->use_chanctx)
local->hw.conf.radar_enabled = ctx->conf.radar_enabled;

@@ -748,6 +752,8 @@ static void ieee80211_del_chanctx(struct ieee80211_local *local,
}

ieee80211_recalc_idle(local);
+
+ ieee80211_remove_wbrf(local->hw.wiphy, &ctx->conf.def);
}

static void ieee80211_free_chanctx(struct ieee80211_local *local,
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 55cdfaef0f5d..539f9cbdda4f 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1395,6 +1395,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
debugfs_hw_add(local);
rate_control_add_debugfs(local);

+ ieee80211_check_wbrf_support(local->hw.wiphy);
+
rtnl_lock();
wiphy_lock(hw->wiphy);

diff --git a/net/mac80211/wbrf.c b/net/mac80211/wbrf.c
new file mode 100644
index 000000000000..91712a927dd7
--- /dev/null
+++ b/net/mac80211/wbrf.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Wifi Band Exclusion Interface
+ * Copyright (C) 2023 Advanced Micro Devices
+ *
+ */
+
+#include <linux/wbrf.h>
+#include <net/cfg80211.h>
+
+#define KHZ_TO_HZ(freq) ((freq) * 1000ULL)
+
+void ieee80211_check_wbrf_support(struct wiphy *wiphy)
+{
+ struct device *dev = wiphy->dev.parent;
+ struct acpi_device *acpi_dev;
+
+ acpi_dev = ACPI_COMPANION(dev);
+ if (!acpi_dev) {
+ dev_dbg(dev, "ACPI companion not found\n");
+ return;
+ }
+
+ wiphy->wbrf_supported = wbrf_supported_producer(acpi_dev);
+ dev_dbg(dev, "WBRF is %s supported\n",
+ wiphy->wbrf_supported ? "" : "not");
+}
+
+static int chan_width_to_mhz(enum nl80211_chan_width chan_width)
+{
+ int mhz;
+
+ switch (chan_width) {
+ case NL80211_CHAN_WIDTH_1:
+ mhz = 1;
+ break;
+ case NL80211_CHAN_WIDTH_2:
+ mhz = 2;
+ break;
+ case NL80211_CHAN_WIDTH_4:
+ mhz = 4;
+ break;
+ case NL80211_CHAN_WIDTH_8:
+ mhz = 8;
+ break;
+ case NL80211_CHAN_WIDTH_16:
+ mhz = 16;
+ break;
+ case NL80211_CHAN_WIDTH_5:
+ mhz = 5;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ mhz = 10;
+ break;
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ mhz = 20;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ mhz = 40;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_80:
+ mhz = 80;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ mhz = 160;
+ break;
+ case NL80211_CHAN_WIDTH_320:
+ mhz = 320;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -1;
+ }
+ return mhz;
+}
+
+static void get_chan_freq_boundary(u32 center_freq,
+ u32 bandwidth,
+ 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);
+}
+
+static int wbrf_get_ranges_from_chandef(struct cfg80211_chan_def *chandef,
+ struct wbrf_ranges_in *ranges_in)
+{
+ u64 start_freq1, end_freq1;
+ u64 start_freq2, end_freq2;
+ int bandwidth;
+
+ bandwidth = chan_width_to_mhz(chandef->width);
+ if (bandwidth < 0)
+ return -EINVAL;
+
+ 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;
+ }
+
+ return 0;
+}
+
+static int wbrf_add_exclusion_wlan(struct acpi_device *adev,
+ struct cfg80211_chan_def *chandef)
+{
+ struct wbrf_ranges_in ranges_in = {0};
+ int ret;
+
+ ret = wbrf_get_ranges_from_chandef(chandef, &ranges_in);
+ if (ret)
+ return ret;
+
+ return wbrf_add_exclusion(adev, &ranges_in);
+}
+
+static int wbrf_remove_exclusion_wlan(struct acpi_device *adev,
+ struct cfg80211_chan_def *chandef)
+{
+ struct wbrf_ranges_in ranges_in = {0};
+ int ret;
+
+ ret = wbrf_get_ranges_from_chandef(chandef, &ranges_in);
+ if (ret)
+ return ret;
+
+ return wbrf_remove_exclusion(adev, &ranges_in);
+}
+
+int ieee80211_add_wbrf(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
+{
+ struct device *dev = wiphy->dev.parent;
+ struct acpi_device *acpi_dev;
+
+ if (!wiphy->wbrf_supported)
+ return 0;
+
+ acpi_dev = ACPI_COMPANION(dev);
+ if (!acpi_dev)
+ return -ENODEV;
+
+ return wbrf_add_exclusion_wlan(acpi_dev, chandef);
+}
+
+void ieee80211_remove_wbrf(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
+{
+ struct device *dev = wiphy->dev.parent;
+ struct acpi_device *acpi_dev;
+
+ if (!wiphy->wbrf_supported)
+ return;
+
+ acpi_dev = ACPI_COMPANION(dev);
+ if (!acpi_dev)
+ return;
+
+ wbrf_remove_exclusion_wlan(acpi_dev, chandef);
+}
--
2.34.1


2023-06-09 07:31:51

by Evan Quan

[permalink] [raw]
Subject: [PATCH V2 7/7] drm/amd/pm: enable Wifi RFI mitigation feature support for SMU13.0.7

Fulfill the SMU13.0.7 support for Wifi RFI mitigation feature.

Signed-off-by: Evan Quan <[email protected]>
---
.../drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c | 59 +++++++++++++++++++
1 file changed, 59 insertions(+)

diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
index 98a33f8ee209..16c1c04e2034 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
@@ -125,6 +125,7 @@ static struct cmn2asic_msg_mapping smu_v13_0_7_message_map[SMU_MSG_MAX_COUNT] =
MSG_MAP(ArmD3, PPSMC_MSG_ArmD3, 0),
MSG_MAP(AllowGpo, PPSMC_MSG_SetGpoAllow, 0),
MSG_MAP(GetPptLimit, PPSMC_MSG_GetPptLimit, 0),
+ MSG_MAP(EnableUCLKShadow, PPSMC_MSG_EnableUCLKShadow, 0),
};

static struct cmn2asic_mapping smu_v13_0_7_clk_map[SMU_CLK_COUNT] = {
@@ -205,6 +206,7 @@ static struct cmn2asic_mapping smu_v13_0_7_table_map[SMU_TABLE_COUNT] = {
TAB_MAP(DRIVER_SMU_CONFIG),
TAB_MAP(ACTIVITY_MONITOR_COEFF),
[SMU_TABLE_COMBO_PPTABLE] = {1, TABLE_COMBO_PPTABLE},
+ TAB_MAP(WIFIBAND),
};

static struct cmn2asic_mapping smu_v13_0_7_pwr_src_map[SMU_POWER_SOURCE_COUNT] = {
@@ -487,6 +489,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)
@@ -1721,6 +1726,57 @@ static int smu_v13_0_7_set_df_cstate(struct smu_context *smu,
NULL);
}

+static bool smu_v13_0_7_wbrf_support_check(struct smu_context *smu)
+{
+ return smu->smc_fw_version > 0x00524600;
+}
+
+static int smu_v13_0_7_set_wbrf_exclusion_ranges(struct smu_context *smu,
+ struct exclusion_range *exclusion_ranges)
+{
+ WifiBandEntryTable_t wifi_bands;
+ int valid_entries = 0;
+ int ret, 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;
+
+ /*
+ * Per confirm with PMFW team, WifiBandEntryNum = 0 is a valid setting.
+ * Considering the scenarios below:
+ * - At first the wifi device adds an exclusion range e.g. (2400,2500) to
+ * BIOS and our driver gets notified. We will set WifiBandEntryNum = 1
+ * and pass the WifiBandEntry (2400, 2500) to PMFW.
+ *
+ * - Later the wifi device removes the wifiband list added above and
+ * our driver gets notified again. At this time, driver will set
+ * WifiBandEntryNum = 0 and pass an empty WifiBandEntry list to PMFW.
+ * - PMFW may still need to do some uclk shadow update(e.g. switching
+ * from shadow clock back to primary clock) on receiving this.
+ */
+
+ 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!");
+
+ return ret;
+}
+
static const struct pptable_funcs smu_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,
@@ -1786,6 +1842,9 @@ static const struct pptable_funcs smu_v13_0_7_ppt_funcs = {
.set_mp1_state = smu_v13_0_7_set_mp1_state,
.set_df_cstate = smu_v13_0_7_set_df_cstate,
.gpo_control = smu_v13_0_gpo_control,
+ .is_asic_wbrf_supported = smu_v13_0_7_wbrf_support_check,
+ .enable_uclk_shadow = smu_v13_0_enable_uclk_shadow,
+ .set_wbrf_exclusion_ranges = smu_v13_0_7_set_wbrf_exclusion_ranges,
};

void smu_v13_0_7_set_ppt_funcs(struct smu_context *smu)
--
2.34.1


2023-06-12 07:42:51

by Evan Quan

[permalink] [raw]
Subject: RE: [PATCH V2 2/7] wifi: mac80211: Add support for ACPI WBRF

[AMD Official Use Only - General]

Thanks Johannes. Comment in-line

> -----Original Message-----
> From: Johannes Berg <[email protected]>
> Sent: Friday, June 9, 2023 4:21 PM
> To: Quan, Evan <[email protected]>; [email protected]; [email protected];
> Deucher, Alexander <[email protected]>; Koenig, Christian
> <[email protected]>; Pan, Xinhui <[email protected]>;
> [email protected]; [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; Limonciello, Mario
> <[email protected]>; Lazar, Lijo <[email protected]>
> Cc: [email protected]; [email protected]; amd-
> [email protected]; [email protected]; linux-
> [email protected]
> Subject: Re: [PATCH V2 2/7] wifi: mac80211: Add support for ACPI WBRF
>
> On Fri, 2023-06-09 at 15:28 +0800, Evan Quan wrote:
>
> > --- a/include/net/cfg80211.h
> > +++ b/include/net/cfg80211.h
> > @@ -5551,6 +5551,10 @@ struct wiphy {
> >
> > u16 hw_timestamp_max_peers;
> >
> > +#ifdef CONFIG_ACPI_WBRF
> > + bool wbrf_supported;
> > +#endif
>
> This should be in some private struct in mac80211, ieee80211_local I think.
There was indeed a proposal from Mario to put this in ieee80211_local.
But I thought "wbrf_supported" stands for a device specific feature and "struct wiphy" seemed the right place.
Anyway I can update this as suggested.
>
> > char priv[] __aligned(NETDEV_ALIGN); };
> >
> > @@ -9067,4 +9071,18 @@ static inline int
> > cfg80211_color_change_notify(struct net_device *dev) bool
> cfg80211_valid_disable_subchannel_bitmap(u16 *bitmap,
> > const struct cfg80211_chan_def
> *chandef);
> >
> > +#ifdef CONFIG_ACPI_WBRF
> > +void ieee80211_check_wbrf_support(struct wiphy *wiphy); int
> > +ieee80211_add_wbrf(struct wiphy *wiphy,
> > + struct cfg80211_chan_def *chandef); void
> > +ieee80211_remove_wbrf(struct wiphy *wiphy,
> > + struct cfg80211_chan_def *chandef); #else static
> inline void
> > +ieee80211_check_wbrf_support(struct wiphy *wiphy) { } static inline
> > +int ieee80211_add_wbrf(struct wiphy *wiphy,
> > + struct cfg80211_chan_def *chandef)
> { return 0; } static
> > +inline void ieee80211_remove_wbrf(struct wiphy *wiphy,
> > + struct cfg80211_chan_def *chandef)
> { } #endif /*
> > +CONFIG_ACPI_WBRF */
>
> Same here, not the right place. This should even be in an internal
> mac80211 header (such as net/mac80211/ieee80211_i.h or create a new
> net/mac80211/wrbf.h or so if you prefer.)
Will update this altogether.
>
>
> > --- a/net/mac80211/chan.c
> > +++ b/net/mac80211/chan.c
> > @@ -668,6 +668,10 @@ static int ieee80211_add_chanctx(struct
> ieee80211_local *local,
> > lockdep_assert_held(&local->mtx);
> > lockdep_assert_held(&local->chanctx_mtx);
> >
> > + err = ieee80211_add_wbrf(local->hw.wiphy, &ctx->conf.def);
> > + if (err)
> > + return err;
> > +
> > if (!local->use_chanctx)
> > local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
> >
> > @@ -748,6 +752,8 @@ static void ieee80211_del_chanctx(struct
> ieee80211_local *local,
> > }
> >
> > ieee80211_recalc_idle(local);
> > +
> > + ieee80211_remove_wbrf(local->hw.wiphy, &ctx->conf.def);
> > }
> >
> > static void ieee80211_free_chanctx(struct ieee80211_local *local,
> >
>
> This is tricky, and quite likely incorrect.
>
> First of all, chandefs can actually _change_, see _ieee80211_change_chanctx().
> You'd probably have to call this add/remove (or have modify) whenever we
> call drv_change_chanctx() to change the width (not if radar/rx chains change).
Thanks for pointing this out. Unfortunately I have limit knowledge about network related.
Can you help me to list all those APIs? Any others except the four below?
_ieee80211_change_chanctx
ieee80211_recalc_chanctx_min_def (Just curious why "!local->use_chanctx" is not cover in this API like others?)
ieee80211_recalc_radar_chanctx (do you mean this one can be ignored ?)
ieee80211_recalc_smps_chanctx
>
> Secondly, you don't know if the driver will actually use ctx->conf.def, or ctx-
> >conf.mindef. For client mode that doesn't matter, but for AP mode if the AP is
> configured to say 160 MHz, it might actually configure down to 20 MHz when
> no stations are connected (or only 20 MHz stations are). I don't know if you
> really care about taking that into account, I also don't know how dynamic this
> really should be. Stations can connect and disconnect quickly, so perhaps the
> WBRF should actually take the full potential bandwidth into account all the
> time, in which case taking
> ctx->conf.def would be correct.
OK. If we cannot figure out what's the exact bandwidth in use for AP, I assume using ctx->conf.def should be OK.
@Limonciello, Mario what's your opinion?
>
> I'll note that your previous in-driver approach had all the same problems the
> way you had implemented it, though I don't know if that driver ever can use
> mindef or not.
>
>
> > +void ieee80211_check_wbrf_support(struct wiphy *wiphy) {
> > + struct device *dev = wiphy->dev.parent;
> > + struct acpi_device *acpi_dev;
> > +
> > + acpi_dev = ACPI_COMPANION(dev);
>
> Can this cope with 'dev' being NULL? Just not sure nothing like hwsim or so
> always even has a parent. I guess it should, but ...
OK, I see. I will add input checks for unexpected NULL.
>
> > +static int chan_width_to_mhz(enum nl80211_chan_width chan_width) {
> > + int mhz;
> > +
> > + switch (chan_width) {
> > + case NL80211_CHAN_WIDTH_1:
> > + mhz = 1;
> > + break;
> > + case NL80211_CHAN_WIDTH_2:
> > + mhz = 2;
> > + break;
> > + case NL80211_CHAN_WIDTH_4:
> > + mhz = 4;
> > + break;
> > + case NL80211_CHAN_WIDTH_8:
> > + mhz = 8;
> > + break;
> > + case NL80211_CHAN_WIDTH_16:
> > + mhz = 16;
> > + break;
> > + case NL80211_CHAN_WIDTH_5:
> > + mhz = 5;
> > + break;
> > + case NL80211_CHAN_WIDTH_10:
> > + mhz = 10;
> > + break;
> > + case NL80211_CHAN_WIDTH_20:
> > + case NL80211_CHAN_WIDTH_20_NOHT:
> > + mhz = 20;
> > + break;
> > + case NL80211_CHAN_WIDTH_40:
> > + mhz = 40;
> > + break;
> > + case NL80211_CHAN_WIDTH_80P80:
> > + case NL80211_CHAN_WIDTH_80:
> > + mhz = 80;
> > + break;
> > + case NL80211_CHAN_WIDTH_160:
> > + mhz = 160;
> > + break;
> > + case NL80211_CHAN_WIDTH_320:
> > + mhz = 320;
> > + break;
> > + default:
> > + WARN_ON_ONCE(1);
> > + return -1;
> > + }
> > + return mhz;
>
> This might be more generally useful as a function in cfg80211 that's exported -
> hwsim has exactly the same function today, for example.
Yes, this shares exactly the same implementation as nl80211_chan_width_to_mhz.
Let me get nl80211_chan_width_to_mhz exposed so that other parts can share.
>
> > +static void get_chan_freq_boundary(u32 center_freq,
> > + u32 bandwidth,
> > + 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);
> > +}
>
> Similar patterns are probably elsewhere too for this, but I guess we can always
> refactor later too.
I did check this before and I did not find similar patterns in other parts of the kernel. Although some of them can provide the information about the center frequency point (e.g. control_freq/ oper_freq). None of them can be used to tell the frequency of the start/end point of the range. Anyway, yes, we can refactor this later if we see such need.

Evan
>
> johannes