All of lore.kernel.org
 help / color / mirror / Atom feed
* [V11 0/8] Enable Wifi RFI interference mitigation feature support
@ 2023-08-31  6:20 ` Evan Quan
  0 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: linux-kernel, linux-acpi, amd-gfx, dri-devel, linux-wireless,
	netdev, Evan Quan

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:      Core functionality setup for WBRF feature support
Patch2 - 3:  Bring WBRF support to wifi subsystem.
Patch4 - 8:  Bring WBRF support to AMD graphics driver.

Evan Quan (8):
  ACPI: Add support for AMD ACPI based Wifi band RFI mitigation feature
  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

 drivers/acpi/Kconfig                          |  17 +
 drivers/acpi/Makefile                         |   2 +
 drivers/acpi/amd_wbrf.c                       | 414 ++++++++++++++++++
 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     | 214 +++++++++
 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                 | 140 ++++++
 include/linux/ieee80211.h                     |   1 +
 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                           | 105 +++++
 net/wireless/chan.c                           |   3 +-
 26 files changed, 1143 insertions(+), 6 deletions(-)
 create mode 100644 drivers/acpi/amd_wbrf.c
 create mode 100644 include/linux/acpi_amd_wbrf.h
 create mode 100644 net/mac80211/wbrf.c

-- 
2.34.1


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [V11 0/8] Enable Wifi RFI interference mitigation feature support
@ 2023-08-31  6:20 ` Evan Quan
  0 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: netdev, linux-wireless, linux-kernel, dri-devel, linux-acpi,
	amd-gfx, Evan Quan

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:      Core functionality setup for WBRF feature support
Patch2 - 3:  Bring WBRF support to wifi subsystem.
Patch4 - 8:  Bring WBRF support to AMD graphics driver.

Evan Quan (8):
  ACPI: Add support for AMD ACPI based Wifi band RFI mitigation feature
  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

 drivers/acpi/Kconfig                          |  17 +
 drivers/acpi/Makefile                         |   2 +
 drivers/acpi/amd_wbrf.c                       | 414 ++++++++++++++++++
 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     | 214 +++++++++
 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                 | 140 ++++++
 include/linux/ieee80211.h                     |   1 +
 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                           | 105 +++++
 net/wireless/chan.c                           |   3 +-
 26 files changed, 1143 insertions(+), 6 deletions(-)
 create mode 100644 drivers/acpi/amd_wbrf.c
 create mode 100644 include/linux/acpi_amd_wbrf.h
 create mode 100644 net/mac80211/wbrf.c

-- 
2.34.1


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [V11 1/8] ACPI: Add support for AMD ACPI based Wifi band RFI mitigation feature
  2023-08-31  6:20 ` Evan Quan
@ 2023-08-31  6:20   ` Evan Quan
  -1 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: linux-kernel, linux-acpi, amd-gfx, dri-devel, linux-wireless,
	netdev, Evan Quan

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.

Signed-off-by: Evan Quan <evan.quan@amd.com>
--
v10->v11:
  - fix typo(Simon)
---
 drivers/acpi/Kconfig          |  17 ++
 drivers/acpi/Makefile         |   2 +
 drivers/acpi/amd_wbrf.c       | 414 ++++++++++++++++++++++++++++++++++
 include/linux/acpi_amd_wbrf.h | 140 ++++++++++++
 4 files changed, 573 insertions(+)
 create mode 100644 drivers/acpi/amd_wbrf.c
 create mode 100644 include/linux/acpi_amd_wbrf.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 00dd309b6682..a092ea72d152 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -594,6 +594,23 @@ config ACPI_PRMT
 	  substantially increase computational overhead related to the
 	  initialization of some server systems.
 
+config WBRF_AMD_ACPI
+	bool "ACPI based WBRF mechanism introduced by AMD"
+	depends on ACPI
+	default n
+	help
+	  Wifi band RFI mitigation mechanism allows multiple drivers from
+	  different domains to notify the frequencies in use so that hardware
+	  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.
+
 endif	# ACPI
 
 config X86_PM_TIMER
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index eaa09bf52f17..a3d2f259d0a5 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -132,3 +132,5 @@ obj-$(CONFIG_ARM64)		+= arm64/
 obj-$(CONFIG_ACPI_VIOT)		+= viot.o
 
 obj-$(CONFIG_RISCV)		+= riscv/
+
+obj-$(CONFIG_WBRF_AMD_ACPI)	+= amd_wbrf.o
diff --git a/drivers/acpi/amd_wbrf.c b/drivers/acpi/amd_wbrf.c
new file mode 100644
index 000000000000..8ee0e2977a30
--- /dev/null
+++ b/drivers/acpi/amd_wbrf.c
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Wifi Band Exclusion Interface (AMD ACPI Implementation)
+ * Copyright (C) 2023 Advanced Micro Devices
+ *
+ */
+
+#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 naturally aligned.
+ * And unfortunately the design has been settled down.
+ */
+struct amd_wbrf_ranges_out {
+	u32			num_of_ranges;
+	struct exclusion_range	band_list[MAX_NUM_OF_WBRF_RANGES];
+} __packed;
+
+static const guid_t wifi_acpi_dsm_guid =
+	GUID_INIT(0x7b7656cf, 0xdc3d, 0x4c1c,
+		  0x83, 0xe9, 0x66, 0xe7, 0x21, 0xde, 0x30, 0x70);
+
+static BLOCKING_NOTIFIER_HEAD(wbrf_chain_head);
+
+static int wbrf_dsm(struct acpi_device *adev,
+		    u8 fn,
+		    union acpi_object *argv4)
+{
+	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_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_out *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++;
+
+	/*
+	 * The valid entry counter does not match with this told.
+	 * Something must went wrong.
+	 */
+	if (num_of_ranges != in->num_of_ranges)
+		return -EINVAL;
+
+	/*
+	 * Every input frequency band comes with two end points(start/end)
+	 * and each is accounted as an element. Meanwhile the range count
+	 * and action type are accounted as an element each.
+	 * So, the total element count = 2 * num_of_ranges + 1 + 1.
+	 */
+	num_of_elements = 2 * num_of_ranges + 1 + 1;
+
+	tmp = kcalloc(num_of_elements, sizeof(*tmp), GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+
+	argv4.package.type = ACPI_TYPE_PACKAGE;
+	argv4.package.count = num_of_elements;
+	argv4.package.elements = tmp;
+
+	tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+	tmp[arg_idx++].integer.value = num_of_ranges;
+	tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+	tmp[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;
+
+		tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+		tmp[arg_idx++].integer.value = in->band_list[loop_idx].start;
+		tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+		tmp[arg_idx++].integer.value = in->band_list[loop_idx].end;
+	}
+
+	ret = wbrf_dsm(adev, WBRF_RECORD, &argv4);
+
+	kfree(tmp);
+
+	return ret;
+}
+
+/**
+ * acpi_amd_wbrf_add_exclusion - broadcast the frequency band the device
+ *                               is using
+ *
+ * @dev: device pointer
+ * @in: input structure containing the frequency band the device is using
+ *
+ * Broadcast to other consumers the frequency band the device starts
+ * to use. Underneath the surface the information is cached into an
+ * internal buffer first. Then a notification is sent to all those
+ * registered consumers. So then they can retrieve that buffer to
+ * know the latest active frequency bands. The benefit with such design
+ * is for those consumers which have not been registered yet, they can
+ * still have a chance to retrieve such information later.
+ */
+int acpi_amd_wbrf_add_exclusion(struct device *dev,
+				struct wbrf_ranges_in_out *in)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+	int ret;
+
+	if (!adev)
+		return -ENODEV;
+
+	ret = wbrf_record(adev, WBRF_RECORD_ADD, in);
+	if (ret)
+		return ret;
+
+	blocking_notifier_call_chain(&wbrf_chain_head,
+				     WBRF_CHANGED,
+				     NULL);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_amd_wbrf_add_exclusion);
+
+/**
+ * acpi_amd_wbrf_remove_exclusion - broadcast the frequency band the device
+ *                                  is no longer using
+ *
+ * @dev: device pointer
+ * @in: input structure containing the frequency band which is not used
+ *      by the device any more
+ *
+ * Broadcast to other consumers the frequency band the device stops
+ * to use. The stored information paired with this will be dropped
+ * from the internal buffer. And then a notification is sent to
+ * all registered consumers.
+ */
+int acpi_amd_wbrf_remove_exclusion(struct device *dev,
+				   struct wbrf_ranges_in_out *in)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+	int ret;
+
+	if (!adev)
+		return -ENODEV;
+
+	ret = wbrf_record(adev, WBRF_RECORD_REMOVE, in);
+	if (ret)
+		return ret;
+
+	blocking_notifier_call_chain(&wbrf_chain_head,
+				     WBRF_CHANGED,
+				     NULL);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_amd_wbrf_remove_exclusion);
+
+static bool acpi_amd_wbrf_supported_system(void)
+{
+	acpi_status status;
+	acpi_handle handle;
+
+	status = acpi_get_handle(NULL, ACPI_AMD_WBRF_METHOD, &handle);
+
+	return ACPI_SUCCESS(status);
+}
+
+/**
+ * acpi_amd_wbrf_supported_producer - determine if the WBRF can be enabled
+ *                                    for the device as a producer
+ *
+ * @dev: device pointer
+ *
+ * Determine if the platform equipped with necessary implementations to
+ * support WBRF for the device as a producer.
+ */
+bool acpi_amd_wbrf_supported_producer(struct device *dev)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+
+	if (!acpi_amd_wbrf_supported_system())
+		return false;
+
+	if (!adev)
+		return false;
+
+	return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid,
+			      WBRF_REVISION,
+			      BIT(WBRF_RECORD));
+}
+EXPORT_SYMBOL_GPL(acpi_amd_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_FAILURE(ret))
+		return NULL;
+
+	return buf.pointer;
+}
+
+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;
+
+	ACPI_FREE(obj);
+
+	return mask & BIT(WBRF_ENABLED) && (mask & funcs) == funcs;
+}
+
+/**
+ * acpi_amd_wbrf_supported_consumer - determine if the WBRF can be enabled
+ *                                    for the device as a consumer
+ *
+ * @dev: device pointer
+ *
+ * Determine if the platform equipped with necessary implementations to
+ * support WBRF for the device as a consumer.
+ */
+bool acpi_amd_wbrf_supported_consumer(struct device *dev)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+
+	if (!acpi_amd_wbrf_supported_system())
+		return false;
+
+	if (!adev)
+		return false;
+
+	return check_acpi_wbrf(adev->handle,
+			       WBRF_REVISION,
+			       BIT(WBRF_RETRIEVE));
+}
+EXPORT_SYMBOL_GPL(acpi_amd_wbrf_supported_consumer);
+
+/**
+ * acpi_amd_wbrf_retrieve_exclusions - retrieve current active frequency
+ *                                     bands
+ *
+ * @dev: device pointer
+ * @out: output structure containing all the active frequency bands
+ *
+ * Retrieve the current active frequency bands which were broadcasted
+ * by other producers. The consumer who calls this API should take
+ * proper actions if any of the frequency band may cause RFI with its
+ * own frequency band used.
+ */
+int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
+				      struct wbrf_ranges_in_out *out)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+	struct amd_wbrf_ranges_out acpi_out = {0};
+	union acpi_object *obj;
+	int ret = 0;
+
+	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, "Wrong sized WBRT information");
+		ret = -EINVAL;
+		goto out;
+	}
+	memcpy(&acpi_out, obj->buffer.pointer, obj->buffer.length);
+
+	out->num_of_ranges = acpi_out.num_of_ranges;
+	memcpy(out->band_list, acpi_out.band_list, sizeof(acpi_out.band_list));
+
+out:
+	ACPI_FREE(obj);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(acpi_amd_wbrf_retrieve_exclusions);
+
+/**
+ * acpi_amd_wbrf_register_notifier - register for notifications of frequency
+ *                                   band update
+ *
+ * @nb: driver notifier block
+ *
+ * The consumer should register itself via this API. So that it can get
+ * notified timely on the frequency band updates from other producers.
+ */
+int acpi_amd_wbrf_register_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&wbrf_chain_head, nb);
+}
+EXPORT_SYMBOL_GPL(acpi_amd_wbrf_register_notifier);
+
+/**
+ * acpi_amd_wbrf_unregister_notifier - unregister for notifications of
+ *                                     frequency band update
+ *
+ * @nb: driver notifier block
+ *
+ * The consumer should call this API when it is longer interested with
+ * the frequency band updates from other producers. Usually, this should
+ * be performed during driver cleanup.
+ */
+int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&wbrf_chain_head, nb);
+}
+EXPORT_SYMBOL_GPL(acpi_amd_wbrf_unregister_notifier);
diff --git a/include/linux/acpi_amd_wbrf.h b/include/linux/acpi_amd_wbrf.h
new file mode 100644
index 000000000000..c2363d664641
--- /dev/null
+++ b/include/linux/acpi_amd_wbrf.h
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Wifi Band Exclusion Interface (AMD ACPI Implementation)
+ * Copyright (C) 2023 Advanced Micro Devices
+ *
+ * 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 to support
+ * WBRF(Wifi Band RFI mitigation Feature) for platforms with AMD dGPU + WLAN.
+ * This needs support from BIOS equipped with necessary AML implementations
+ * and dGPU firmwares.
+ *
+ * Some general terms:
+ * Producer: such component who can produce high-powered radio frequency
+ * Consumer: such component who can adjust its in-use frequency in
+ *           response to the radio frequencies of other components to
+ *           mitigate the possible RFI.
+ *
+ * To make the mechanism function, those producers should notify active use
+ * of their particular frequencies so that other consumers can make relative
+ * internal adjustments as necessary to avoid this resonance.
+ */
+
+#ifndef _ACPI_AMD_WBRF_H
+#define _ACPI_AMD_WBRF_H
+
+#include <linux/device.h>
+#include <linux/notifier.h>
+
+/*
+ * A wbrf range is defined as a frequency band with start and end
+ * frequency point specified(in Hz). And a vaild range should have
+ * its start and end frequency point filled with non-zero values.
+ * Meanwhile, the maximum number of wbrf ranges is limited as
+ * `MAX_NUM_OF_WBRF_RANGES`.
+ */
+#define MAX_NUM_OF_WBRF_RANGES		11
+
+struct exclusion_range {
+	u64		start;
+	u64		end;
+};
+
+struct wbrf_ranges_in_out {
+	u64			num_of_ranges;
+	struct exclusion_range	band_list[MAX_NUM_OF_WBRF_RANGES];
+};
+
+/*
+ * The notification types for the consumers are defined as below.
+ * The consumers may need to take different actions in response to
+ * different notifications.
+ * WBRF_CHANGED: there was some frequency band updates. The consumers
+ *               should retrieve the latest active frequency bands.
+ */
+enum wbrf_notifier_actions {
+	WBRF_CHANGED,
+};
+
+#if IS_ENABLED(CONFIG_WBRF_AMD_ACPI)
+/*
+ * The expected flow for the producers:
+ * 1) During probe, call `acpi_amd_wbrf_supported_producer` to check
+ *    if WBRF can be enabled for the device.
+ * 2) On using some frequency band, call `acpi_amd_wbrf_add_exclusion`
+ *    to get other consumers properly notified.
+ * 3) Or on stopping using some frequency band, call
+ *    `acpi_amd_wbrf_remove_exclusion` to get other consumers notified.
+ */
+bool acpi_amd_wbrf_supported_producer(struct device *dev);
+int acpi_amd_wbrf_remove_exclusion(struct device *dev,
+				   struct wbrf_ranges_in_out *in);
+int acpi_amd_wbrf_add_exclusion(struct device *dev,
+				struct wbrf_ranges_in_out *in);
+
+/*
+ * The expected flow for the consumers:
+ * 1) During probe, call `acpi_amd_wbrf_supported_consumer` to check if WBRF
+ *    can be enabled for the device.
+ * 2) Call `acpi_amd_wbrf_register_notifier` to register for notification
+ *    of frequency band change(add or remove) from other producers.
+ * 3) Call the `acpi_amd_wbrf_retrieve_exclusions` intentionally to retrieve
+ *    current active frequency bands considering some producers may broadcast
+ *    such information before the consumer is up.
+ * 4) On receiving a notification for frequency band change, run
+ *    `acpi_amd_wbrf_retrieve_exclusions` again to retrieve the latest
+ *    active frequency bands.
+ * 5) During driver cleanup, call `acpi_amd_wbrf_unregister_notifier` to
+ *    unregister the notifier.
+ */
+bool acpi_amd_wbrf_supported_consumer(struct device *dev);
+int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
+				      struct wbrf_ranges_in_out *out);
+int acpi_amd_wbrf_register_notifier(struct notifier_block *nb);
+int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb);
+#else
+static inline
+bool acpi_amd_wbrf_supported_consumer(struct device *dev)
+{
+	return false;
+}
+static inline
+int acpi_amd_wbrf_remove_exclusion(struct device *dev,
+				   struct wbrf_ranges_in_out *in)
+{
+	return -ENODEV;
+}
+static inline
+int acpi_amd_wbrf_add_exclusion(struct device *dev,
+				struct wbrf_ranges_in_out *in)
+{
+	return -ENODEV;
+}
+static inline
+bool acpi_amd_wbrf_supported_producer(struct device *dev)
+{
+	return false;
+}
+static inline
+int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
+				      struct wbrf_ranges_in_out *out)
+{
+	return -ENODEV;
+}
+static inline
+int acpi_amd_wbrf_register_notifier(struct notifier_block *nb)
+{
+	return -ENODEV;
+}
+static inline
+int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb)
+{
+	return -ENODEV;
+}
+#endif
+
+#endif /* _ACPI_AMD_WBRF_H */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [V11 1/8] ACPI: Add support for AMD ACPI based Wifi band RFI mitigation feature
@ 2023-08-31  6:20   ` Evan Quan
  0 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: netdev, linux-wireless, linux-kernel, dri-devel, linux-acpi,
	amd-gfx, Evan Quan

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.

Signed-off-by: Evan Quan <evan.quan@amd.com>
--
v10->v11:
  - fix typo(Simon)
---
 drivers/acpi/Kconfig          |  17 ++
 drivers/acpi/Makefile         |   2 +
 drivers/acpi/amd_wbrf.c       | 414 ++++++++++++++++++++++++++++++++++
 include/linux/acpi_amd_wbrf.h | 140 ++++++++++++
 4 files changed, 573 insertions(+)
 create mode 100644 drivers/acpi/amd_wbrf.c
 create mode 100644 include/linux/acpi_amd_wbrf.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 00dd309b6682..a092ea72d152 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -594,6 +594,23 @@ config ACPI_PRMT
 	  substantially increase computational overhead related to the
 	  initialization of some server systems.
 
+config WBRF_AMD_ACPI
+	bool "ACPI based WBRF mechanism introduced by AMD"
+	depends on ACPI
+	default n
+	help
+	  Wifi band RFI mitigation mechanism allows multiple drivers from
+	  different domains to notify the frequencies in use so that hardware
+	  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.
+
 endif	# ACPI
 
 config X86_PM_TIMER
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index eaa09bf52f17..a3d2f259d0a5 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -132,3 +132,5 @@ obj-$(CONFIG_ARM64)		+= arm64/
 obj-$(CONFIG_ACPI_VIOT)		+= viot.o
 
 obj-$(CONFIG_RISCV)		+= riscv/
+
+obj-$(CONFIG_WBRF_AMD_ACPI)	+= amd_wbrf.o
diff --git a/drivers/acpi/amd_wbrf.c b/drivers/acpi/amd_wbrf.c
new file mode 100644
index 000000000000..8ee0e2977a30
--- /dev/null
+++ b/drivers/acpi/amd_wbrf.c
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Wifi Band Exclusion Interface (AMD ACPI Implementation)
+ * Copyright (C) 2023 Advanced Micro Devices
+ *
+ */
+
+#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 naturally aligned.
+ * And unfortunately the design has been settled down.
+ */
+struct amd_wbrf_ranges_out {
+	u32			num_of_ranges;
+	struct exclusion_range	band_list[MAX_NUM_OF_WBRF_RANGES];
+} __packed;
+
+static const guid_t wifi_acpi_dsm_guid =
+	GUID_INIT(0x7b7656cf, 0xdc3d, 0x4c1c,
+		  0x83, 0xe9, 0x66, 0xe7, 0x21, 0xde, 0x30, 0x70);
+
+static BLOCKING_NOTIFIER_HEAD(wbrf_chain_head);
+
+static int wbrf_dsm(struct acpi_device *adev,
+		    u8 fn,
+		    union acpi_object *argv4)
+{
+	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_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_out *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++;
+
+	/*
+	 * The valid entry counter does not match with this told.
+	 * Something must went wrong.
+	 */
+	if (num_of_ranges != in->num_of_ranges)
+		return -EINVAL;
+
+	/*
+	 * Every input frequency band comes with two end points(start/end)
+	 * and each is accounted as an element. Meanwhile the range count
+	 * and action type are accounted as an element each.
+	 * So, the total element count = 2 * num_of_ranges + 1 + 1.
+	 */
+	num_of_elements = 2 * num_of_ranges + 1 + 1;
+
+	tmp = kcalloc(num_of_elements, sizeof(*tmp), GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+
+	argv4.package.type = ACPI_TYPE_PACKAGE;
+	argv4.package.count = num_of_elements;
+	argv4.package.elements = tmp;
+
+	tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+	tmp[arg_idx++].integer.value = num_of_ranges;
+	tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+	tmp[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;
+
+		tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+		tmp[arg_idx++].integer.value = in->band_list[loop_idx].start;
+		tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+		tmp[arg_idx++].integer.value = in->band_list[loop_idx].end;
+	}
+
+	ret = wbrf_dsm(adev, WBRF_RECORD, &argv4);
+
+	kfree(tmp);
+
+	return ret;
+}
+
+/**
+ * acpi_amd_wbrf_add_exclusion - broadcast the frequency band the device
+ *                               is using
+ *
+ * @dev: device pointer
+ * @in: input structure containing the frequency band the device is using
+ *
+ * Broadcast to other consumers the frequency band the device starts
+ * to use. Underneath the surface the information is cached into an
+ * internal buffer first. Then a notification is sent to all those
+ * registered consumers. So then they can retrieve that buffer to
+ * know the latest active frequency bands. The benefit with such design
+ * is for those consumers which have not been registered yet, they can
+ * still have a chance to retrieve such information later.
+ */
+int acpi_amd_wbrf_add_exclusion(struct device *dev,
+				struct wbrf_ranges_in_out *in)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+	int ret;
+
+	if (!adev)
+		return -ENODEV;
+
+	ret = wbrf_record(adev, WBRF_RECORD_ADD, in);
+	if (ret)
+		return ret;
+
+	blocking_notifier_call_chain(&wbrf_chain_head,
+				     WBRF_CHANGED,
+				     NULL);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_amd_wbrf_add_exclusion);
+
+/**
+ * acpi_amd_wbrf_remove_exclusion - broadcast the frequency band the device
+ *                                  is no longer using
+ *
+ * @dev: device pointer
+ * @in: input structure containing the frequency band which is not used
+ *      by the device any more
+ *
+ * Broadcast to other consumers the frequency band the device stops
+ * to use. The stored information paired with this will be dropped
+ * from the internal buffer. And then a notification is sent to
+ * all registered consumers.
+ */
+int acpi_amd_wbrf_remove_exclusion(struct device *dev,
+				   struct wbrf_ranges_in_out *in)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+	int ret;
+
+	if (!adev)
+		return -ENODEV;
+
+	ret = wbrf_record(adev, WBRF_RECORD_REMOVE, in);
+	if (ret)
+		return ret;
+
+	blocking_notifier_call_chain(&wbrf_chain_head,
+				     WBRF_CHANGED,
+				     NULL);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_amd_wbrf_remove_exclusion);
+
+static bool acpi_amd_wbrf_supported_system(void)
+{
+	acpi_status status;
+	acpi_handle handle;
+
+	status = acpi_get_handle(NULL, ACPI_AMD_WBRF_METHOD, &handle);
+
+	return ACPI_SUCCESS(status);
+}
+
+/**
+ * acpi_amd_wbrf_supported_producer - determine if the WBRF can be enabled
+ *                                    for the device as a producer
+ *
+ * @dev: device pointer
+ *
+ * Determine if the platform equipped with necessary implementations to
+ * support WBRF for the device as a producer.
+ */
+bool acpi_amd_wbrf_supported_producer(struct device *dev)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+
+	if (!acpi_amd_wbrf_supported_system())
+		return false;
+
+	if (!adev)
+		return false;
+
+	return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid,
+			      WBRF_REVISION,
+			      BIT(WBRF_RECORD));
+}
+EXPORT_SYMBOL_GPL(acpi_amd_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_FAILURE(ret))
+		return NULL;
+
+	return buf.pointer;
+}
+
+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;
+
+	ACPI_FREE(obj);
+
+	return mask & BIT(WBRF_ENABLED) && (mask & funcs) == funcs;
+}
+
+/**
+ * acpi_amd_wbrf_supported_consumer - determine if the WBRF can be enabled
+ *                                    for the device as a consumer
+ *
+ * @dev: device pointer
+ *
+ * Determine if the platform equipped with necessary implementations to
+ * support WBRF for the device as a consumer.
+ */
+bool acpi_amd_wbrf_supported_consumer(struct device *dev)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+
+	if (!acpi_amd_wbrf_supported_system())
+		return false;
+
+	if (!adev)
+		return false;
+
+	return check_acpi_wbrf(adev->handle,
+			       WBRF_REVISION,
+			       BIT(WBRF_RETRIEVE));
+}
+EXPORT_SYMBOL_GPL(acpi_amd_wbrf_supported_consumer);
+
+/**
+ * acpi_amd_wbrf_retrieve_exclusions - retrieve current active frequency
+ *                                     bands
+ *
+ * @dev: device pointer
+ * @out: output structure containing all the active frequency bands
+ *
+ * Retrieve the current active frequency bands which were broadcasted
+ * by other producers. The consumer who calls this API should take
+ * proper actions if any of the frequency band may cause RFI with its
+ * own frequency band used.
+ */
+int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
+				      struct wbrf_ranges_in_out *out)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+	struct amd_wbrf_ranges_out acpi_out = {0};
+	union acpi_object *obj;
+	int ret = 0;
+
+	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, "Wrong sized WBRT information");
+		ret = -EINVAL;
+		goto out;
+	}
+	memcpy(&acpi_out, obj->buffer.pointer, obj->buffer.length);
+
+	out->num_of_ranges = acpi_out.num_of_ranges;
+	memcpy(out->band_list, acpi_out.band_list, sizeof(acpi_out.band_list));
+
+out:
+	ACPI_FREE(obj);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(acpi_amd_wbrf_retrieve_exclusions);
+
+/**
+ * acpi_amd_wbrf_register_notifier - register for notifications of frequency
+ *                                   band update
+ *
+ * @nb: driver notifier block
+ *
+ * The consumer should register itself via this API. So that it can get
+ * notified timely on the frequency band updates from other producers.
+ */
+int acpi_amd_wbrf_register_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&wbrf_chain_head, nb);
+}
+EXPORT_SYMBOL_GPL(acpi_amd_wbrf_register_notifier);
+
+/**
+ * acpi_amd_wbrf_unregister_notifier - unregister for notifications of
+ *                                     frequency band update
+ *
+ * @nb: driver notifier block
+ *
+ * The consumer should call this API when it is longer interested with
+ * the frequency band updates from other producers. Usually, this should
+ * be performed during driver cleanup.
+ */
+int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&wbrf_chain_head, nb);
+}
+EXPORT_SYMBOL_GPL(acpi_amd_wbrf_unregister_notifier);
diff --git a/include/linux/acpi_amd_wbrf.h b/include/linux/acpi_amd_wbrf.h
new file mode 100644
index 000000000000..c2363d664641
--- /dev/null
+++ b/include/linux/acpi_amd_wbrf.h
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Wifi Band Exclusion Interface (AMD ACPI Implementation)
+ * Copyright (C) 2023 Advanced Micro Devices
+ *
+ * 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 to support
+ * WBRF(Wifi Band RFI mitigation Feature) for platforms with AMD dGPU + WLAN.
+ * This needs support from BIOS equipped with necessary AML implementations
+ * and dGPU firmwares.
+ *
+ * Some general terms:
+ * Producer: such component who can produce high-powered radio frequency
+ * Consumer: such component who can adjust its in-use frequency in
+ *           response to the radio frequencies of other components to
+ *           mitigate the possible RFI.
+ *
+ * To make the mechanism function, those producers should notify active use
+ * of their particular frequencies so that other consumers can make relative
+ * internal adjustments as necessary to avoid this resonance.
+ */
+
+#ifndef _ACPI_AMD_WBRF_H
+#define _ACPI_AMD_WBRF_H
+
+#include <linux/device.h>
+#include <linux/notifier.h>
+
+/*
+ * A wbrf range is defined as a frequency band with start and end
+ * frequency point specified(in Hz). And a vaild range should have
+ * its start and end frequency point filled with non-zero values.
+ * Meanwhile, the maximum number of wbrf ranges is limited as
+ * `MAX_NUM_OF_WBRF_RANGES`.
+ */
+#define MAX_NUM_OF_WBRF_RANGES		11
+
+struct exclusion_range {
+	u64		start;
+	u64		end;
+};
+
+struct wbrf_ranges_in_out {
+	u64			num_of_ranges;
+	struct exclusion_range	band_list[MAX_NUM_OF_WBRF_RANGES];
+};
+
+/*
+ * The notification types for the consumers are defined as below.
+ * The consumers may need to take different actions in response to
+ * different notifications.
+ * WBRF_CHANGED: there was some frequency band updates. The consumers
+ *               should retrieve the latest active frequency bands.
+ */
+enum wbrf_notifier_actions {
+	WBRF_CHANGED,
+};
+
+#if IS_ENABLED(CONFIG_WBRF_AMD_ACPI)
+/*
+ * The expected flow for the producers:
+ * 1) During probe, call `acpi_amd_wbrf_supported_producer` to check
+ *    if WBRF can be enabled for the device.
+ * 2) On using some frequency band, call `acpi_amd_wbrf_add_exclusion`
+ *    to get other consumers properly notified.
+ * 3) Or on stopping using some frequency band, call
+ *    `acpi_amd_wbrf_remove_exclusion` to get other consumers notified.
+ */
+bool acpi_amd_wbrf_supported_producer(struct device *dev);
+int acpi_amd_wbrf_remove_exclusion(struct device *dev,
+				   struct wbrf_ranges_in_out *in);
+int acpi_amd_wbrf_add_exclusion(struct device *dev,
+				struct wbrf_ranges_in_out *in);
+
+/*
+ * The expected flow for the consumers:
+ * 1) During probe, call `acpi_amd_wbrf_supported_consumer` to check if WBRF
+ *    can be enabled for the device.
+ * 2) Call `acpi_amd_wbrf_register_notifier` to register for notification
+ *    of frequency band change(add or remove) from other producers.
+ * 3) Call the `acpi_amd_wbrf_retrieve_exclusions` intentionally to retrieve
+ *    current active frequency bands considering some producers may broadcast
+ *    such information before the consumer is up.
+ * 4) On receiving a notification for frequency band change, run
+ *    `acpi_amd_wbrf_retrieve_exclusions` again to retrieve the latest
+ *    active frequency bands.
+ * 5) During driver cleanup, call `acpi_amd_wbrf_unregister_notifier` to
+ *    unregister the notifier.
+ */
+bool acpi_amd_wbrf_supported_consumer(struct device *dev);
+int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
+				      struct wbrf_ranges_in_out *out);
+int acpi_amd_wbrf_register_notifier(struct notifier_block *nb);
+int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb);
+#else
+static inline
+bool acpi_amd_wbrf_supported_consumer(struct device *dev)
+{
+	return false;
+}
+static inline
+int acpi_amd_wbrf_remove_exclusion(struct device *dev,
+				   struct wbrf_ranges_in_out *in)
+{
+	return -ENODEV;
+}
+static inline
+int acpi_amd_wbrf_add_exclusion(struct device *dev,
+				struct wbrf_ranges_in_out *in)
+{
+	return -ENODEV;
+}
+static inline
+bool acpi_amd_wbrf_supported_producer(struct device *dev)
+{
+	return false;
+}
+static inline
+int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
+				      struct wbrf_ranges_in_out *out)
+{
+	return -ENODEV;
+}
+static inline
+int acpi_amd_wbrf_register_notifier(struct notifier_block *nb)
+{
+	return -ENODEV;
+}
+static inline
+int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb)
+{
+	return -ENODEV;
+}
+#endif
+
+#endif /* _ACPI_AMD_WBRF_H */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [V11 2/8] cfg80211: expose nl80211_chan_width_to_mhz for wide sharing
  2023-08-31  6:20 ` Evan Quan
@ 2023-08-31  6:20   ` Evan Quan
  -1 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: linux-kernel, linux-acpi, amd-gfx, dri-devel, linux-wireless,
	netdev, Evan Quan

The newly added WBRF feature needs this interface for channel
width calculation.

Signed-off-by: Evan Quan <evan.quan@amd.com>
--
v8->v9:
  - correct typo(Mhz -> MHz) (Johnson)
---
 include/net/cfg80211.h | 8 ++++++++
 net/wireless/chan.c    | 3 ++-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 7c7d03aa9d06..8c2a9b748621 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -920,6 +920,14 @@ const struct cfg80211_chan_def *
 cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1,
 			    const struct cfg80211_chan_def *chandef2);
 
+/**
+ * nl80211_chan_width_to_mhz - get the channel width in MHz
+ * @chan_width: the channel width from &enum nl80211_chan_width
+ * Return: channel width in MHz if the chan_width from &enum nl80211_chan_width
+ * is valid. -1 otherwise.
+ */
+int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width);
+
 /**
  * cfg80211_chandef_valid - check if a channel definition is valid
  * @chandef: the channel definition to check
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 0b7e81db383d..227db04eac42 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -141,7 +141,7 @@ static bool cfg80211_edmg_chandef_valid(const struct cfg80211_chan_def *chandef)
 	return true;
 }
 
-static int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width)
+int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width)
 {
 	int mhz;
 
@@ -190,6 +190,7 @@ static int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width)
 	}
 	return mhz;
 }
+EXPORT_SYMBOL(nl80211_chan_width_to_mhz);
 
 static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
 {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [V11 2/8] cfg80211: expose nl80211_chan_width_to_mhz for wide sharing
@ 2023-08-31  6:20   ` Evan Quan
  0 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: netdev, linux-wireless, linux-kernel, dri-devel, linux-acpi,
	amd-gfx, Evan Quan

The newly added WBRF feature needs this interface for channel
width calculation.

Signed-off-by: Evan Quan <evan.quan@amd.com>
--
v8->v9:
  - correct typo(Mhz -> MHz) (Johnson)
---
 include/net/cfg80211.h | 8 ++++++++
 net/wireless/chan.c    | 3 ++-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 7c7d03aa9d06..8c2a9b748621 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -920,6 +920,14 @@ const struct cfg80211_chan_def *
 cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1,
 			    const struct cfg80211_chan_def *chandef2);
 
+/**
+ * nl80211_chan_width_to_mhz - get the channel width in MHz
+ * @chan_width: the channel width from &enum nl80211_chan_width
+ * Return: channel width in MHz if the chan_width from &enum nl80211_chan_width
+ * is valid. -1 otherwise.
+ */
+int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width);
+
 /**
  * cfg80211_chandef_valid - check if a channel definition is valid
  * @chandef: the channel definition to check
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 0b7e81db383d..227db04eac42 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -141,7 +141,7 @@ static bool cfg80211_edmg_chandef_valid(const struct cfg80211_chan_def *chandef)
 	return true;
 }
 
-static int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width)
+int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width)
 {
 	int mhz;
 
@@ -190,6 +190,7 @@ static int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width)
 	}
 	return mhz;
 }
+EXPORT_SYMBOL(nl80211_chan_width_to_mhz);
 
 static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
 {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [V11 3/8] wifi: mac80211: Add support for WBRF features
  2023-08-31  6:20 ` Evan Quan
@ 2023-08-31  6:20   ` Evan Quan
  -1 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: linux-kernel, linux-acpi, amd-gfx, dri-devel, linux-wireless,
	netdev, Evan Quan

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 <evan.quan@amd.com>
Signed-off-by: Evan Quan <evan.quan@amd.com>
--
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)
v9->v10:
  - get ranges_in->num_of_ranges set and passed in(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        | 105 +++++++++++++++++++++++++++++++++++++
 6 files changed, 128 insertions(+)
 create mode 100644 net/mac80211/wbrf.c

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 4b998090898e..f995d06da87f 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -4335,6 +4335,7 @@ static inline int ieee80211_get_tdls_action(struct sk_buff *skb, u32 hdr_size)
 /* convert frequencies */
 #define MHZ_TO_KHZ(freq) ((freq) * 1000)
 #define KHZ_TO_MHZ(freq) ((freq) / 1000)
+#define KHZ_TO_HZ(freq)  ((freq) * 1000)
 #define PR_KHZ(f) KHZ_TO_MHZ(f), f % 1000
 #define KHZ_F "%d.%03d"
 
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index b8de44da1fb8..d46c36f55fd3 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-y += wbrf.o
+
 ccflags-y += -DDEBUG
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 68952752b599..458469c224ae 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -506,11 +506,16 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local,
 
 	WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
 
+	ieee80211_remove_wbrf(local, &ctx->conf.def);
+
 	ctx->conf.def = *chandef;
 
 	/* check if min chanctx also changed */
 	changed = IEEE80211_CHANCTX_CHANGE_WIDTH |
 		  _ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for);
+
+	ieee80211_add_wbrf(local, &ctx->conf.def);
+
 	drv_change_chanctx(local, ctx, changed);
 
 	if (!local->use_chanctx) {
@@ -668,6 +673,8 @@ static int ieee80211_add_chanctx(struct ieee80211_local *local,
 	lockdep_assert_held(&local->mtx);
 	lockdep_assert_held(&local->chanctx_mtx);
 
+	ieee80211_add_wbrf(local, &ctx->conf.def);
+
 	if (!local->use_chanctx)
 		local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
 
@@ -748,6 +755,8 @@ static void ieee80211_del_chanctx(struct ieee80211_local *local,
 	}
 
 	ieee80211_recalc_idle(local);
+
+	ieee80211_remove_wbrf(local, &ctx->conf.def);
 }
 
 static void ieee80211_free_chanctx(struct ieee80211_local *local,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 91633a0b723e..719f2c892132 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1600,6 +1600,8 @@ struct ieee80211_local {
 
 	/* extended capabilities provided by mac80211 */
 	u8 ext_capa[8];
+
+	bool wbrf_supported;
 };
 
 static inline struct ieee80211_sub_if_data *
@@ -2638,4 +2640,11 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata,
 				    const struct ieee80211_eht_cap_elem *eht_cap_ie_elem,
 				    u8 eht_cap_len,
 				    struct link_sta_info *link_sta);
+
+void ieee80211_check_wbrf_support(struct ieee80211_local *local);
+void ieee80211_add_wbrf(struct ieee80211_local *local,
+			struct cfg80211_chan_def *chandef);
+void ieee80211_remove_wbrf(struct ieee80211_local *local,
+			   struct cfg80211_chan_def *chandef);
+
 #endif /* IEEE80211_I_H */
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 24315d7b3126..b20bdaac84db 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1396,6 +1396,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 	debugfs_hw_add(local);
 	rate_control_add_debugfs(local);
 
+	ieee80211_check_wbrf_support(local);
+
 	rtnl_lock();
 	wiphy_lock(hw->wiphy);
 
diff --git a/net/mac80211/wbrf.c b/net/mac80211/wbrf.c
new file mode 100644
index 000000000000..63978c7d2bcb
--- /dev/null
+++ b/net/mac80211/wbrf.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Wifi Band Exclusion Interface for WWAN
+ * Copyright (C) 2023 Advanced Micro Devices
+ *
+ */
+
+#include <linux/acpi_amd_wbrf.h>
+#include <net/cfg80211.h>
+#include "ieee80211_i.h"
+
+void ieee80211_check_wbrf_support(struct ieee80211_local *local)
+{
+	struct wiphy *wiphy = local->hw.wiphy;
+	struct device *dev;
+
+	if (!wiphy)
+		return;
+
+	dev = wiphy->dev.parent;
+	if (!dev)
+		return;
+
+	local->wbrf_supported = acpi_amd_wbrf_supported_producer(dev);
+	dev_dbg(dev, "WBRF is %s supported\n",
+		local->wbrf_supported ? "" : "not");
+}
+
+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 void get_ranges_from_chandef(struct cfg80211_chan_def *chandef,
+				    struct wbrf_ranges_in_out *ranges_in)
+{
+	u64 start_freq1, end_freq1;
+	u64 start_freq2, end_freq2;
+	int bandwidth;
+
+	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;
+	ranges_in->num_of_ranges = 1;
+
+	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;
+		ranges_in->num_of_ranges++;
+	}
+}
+
+void ieee80211_add_wbrf(struct ieee80211_local *local,
+			struct cfg80211_chan_def *chandef)
+{
+	struct wbrf_ranges_in_out ranges_in = {0};
+	struct device *dev;
+
+	if (!local->wbrf_supported)
+		return;
+
+	dev = local->hw.wiphy->dev.parent;
+
+	get_ranges_from_chandef(chandef, &ranges_in);
+
+	acpi_amd_wbrf_add_exclusion(dev, &ranges_in);
+}
+
+void ieee80211_remove_wbrf(struct ieee80211_local *local,
+			   struct cfg80211_chan_def *chandef)
+{
+	struct wbrf_ranges_in_out ranges_in = {0};
+	struct device *dev;
+
+	if (!local->wbrf_supported)
+		return;
+
+	dev = local->hw.wiphy->dev.parent;
+
+	get_ranges_from_chandef(chandef, &ranges_in);
+
+	acpi_amd_wbrf_remove_exclusion(dev, &ranges_in);
+}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [V11 3/8] wifi: mac80211: Add support for WBRF features
@ 2023-08-31  6:20   ` Evan Quan
  0 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: netdev, linux-wireless, linux-kernel, dri-devel, linux-acpi,
	amd-gfx, Evan Quan

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 <evan.quan@amd.com>
Signed-off-by: Evan Quan <evan.quan@amd.com>
--
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)
v9->v10:
  - get ranges_in->num_of_ranges set and passed in(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        | 105 +++++++++++++++++++++++++++++++++++++
 6 files changed, 128 insertions(+)
 create mode 100644 net/mac80211/wbrf.c

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 4b998090898e..f995d06da87f 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -4335,6 +4335,7 @@ static inline int ieee80211_get_tdls_action(struct sk_buff *skb, u32 hdr_size)
 /* convert frequencies */
 #define MHZ_TO_KHZ(freq) ((freq) * 1000)
 #define KHZ_TO_MHZ(freq) ((freq) / 1000)
+#define KHZ_TO_HZ(freq)  ((freq) * 1000)
 #define PR_KHZ(f) KHZ_TO_MHZ(f), f % 1000
 #define KHZ_F "%d.%03d"
 
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index b8de44da1fb8..d46c36f55fd3 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-y += wbrf.o
+
 ccflags-y += -DDEBUG
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 68952752b599..458469c224ae 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -506,11 +506,16 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local,
 
 	WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
 
+	ieee80211_remove_wbrf(local, &ctx->conf.def);
+
 	ctx->conf.def = *chandef;
 
 	/* check if min chanctx also changed */
 	changed = IEEE80211_CHANCTX_CHANGE_WIDTH |
 		  _ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for);
+
+	ieee80211_add_wbrf(local, &ctx->conf.def);
+
 	drv_change_chanctx(local, ctx, changed);
 
 	if (!local->use_chanctx) {
@@ -668,6 +673,8 @@ static int ieee80211_add_chanctx(struct ieee80211_local *local,
 	lockdep_assert_held(&local->mtx);
 	lockdep_assert_held(&local->chanctx_mtx);
 
+	ieee80211_add_wbrf(local, &ctx->conf.def);
+
 	if (!local->use_chanctx)
 		local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
 
@@ -748,6 +755,8 @@ static void ieee80211_del_chanctx(struct ieee80211_local *local,
 	}
 
 	ieee80211_recalc_idle(local);
+
+	ieee80211_remove_wbrf(local, &ctx->conf.def);
 }
 
 static void ieee80211_free_chanctx(struct ieee80211_local *local,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 91633a0b723e..719f2c892132 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1600,6 +1600,8 @@ struct ieee80211_local {
 
 	/* extended capabilities provided by mac80211 */
 	u8 ext_capa[8];
+
+	bool wbrf_supported;
 };
 
 static inline struct ieee80211_sub_if_data *
@@ -2638,4 +2640,11 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata,
 				    const struct ieee80211_eht_cap_elem *eht_cap_ie_elem,
 				    u8 eht_cap_len,
 				    struct link_sta_info *link_sta);
+
+void ieee80211_check_wbrf_support(struct ieee80211_local *local);
+void ieee80211_add_wbrf(struct ieee80211_local *local,
+			struct cfg80211_chan_def *chandef);
+void ieee80211_remove_wbrf(struct ieee80211_local *local,
+			   struct cfg80211_chan_def *chandef);
+
 #endif /* IEEE80211_I_H */
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 24315d7b3126..b20bdaac84db 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1396,6 +1396,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 	debugfs_hw_add(local);
 	rate_control_add_debugfs(local);
 
+	ieee80211_check_wbrf_support(local);
+
 	rtnl_lock();
 	wiphy_lock(hw->wiphy);
 
diff --git a/net/mac80211/wbrf.c b/net/mac80211/wbrf.c
new file mode 100644
index 000000000000..63978c7d2bcb
--- /dev/null
+++ b/net/mac80211/wbrf.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Wifi Band Exclusion Interface for WWAN
+ * Copyright (C) 2023 Advanced Micro Devices
+ *
+ */
+
+#include <linux/acpi_amd_wbrf.h>
+#include <net/cfg80211.h>
+#include "ieee80211_i.h"
+
+void ieee80211_check_wbrf_support(struct ieee80211_local *local)
+{
+	struct wiphy *wiphy = local->hw.wiphy;
+	struct device *dev;
+
+	if (!wiphy)
+		return;
+
+	dev = wiphy->dev.parent;
+	if (!dev)
+		return;
+
+	local->wbrf_supported = acpi_amd_wbrf_supported_producer(dev);
+	dev_dbg(dev, "WBRF is %s supported\n",
+		local->wbrf_supported ? "" : "not");
+}
+
+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 void get_ranges_from_chandef(struct cfg80211_chan_def *chandef,
+				    struct wbrf_ranges_in_out *ranges_in)
+{
+	u64 start_freq1, end_freq1;
+	u64 start_freq2, end_freq2;
+	int bandwidth;
+
+	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;
+	ranges_in->num_of_ranges = 1;
+
+	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;
+		ranges_in->num_of_ranges++;
+	}
+}
+
+void ieee80211_add_wbrf(struct ieee80211_local *local,
+			struct cfg80211_chan_def *chandef)
+{
+	struct wbrf_ranges_in_out ranges_in = {0};
+	struct device *dev;
+
+	if (!local->wbrf_supported)
+		return;
+
+	dev = local->hw.wiphy->dev.parent;
+
+	get_ranges_from_chandef(chandef, &ranges_in);
+
+	acpi_amd_wbrf_add_exclusion(dev, &ranges_in);
+}
+
+void ieee80211_remove_wbrf(struct ieee80211_local *local,
+			   struct cfg80211_chan_def *chandef)
+{
+	struct wbrf_ranges_in_out ranges_in = {0};
+	struct device *dev;
+
+	if (!local->wbrf_supported)
+		return;
+
+	dev = local->hw.wiphy->dev.parent;
+
+	get_ranges_from_chandef(chandef, &ranges_in);
+
+	acpi_amd_wbrf_remove_exclusion(dev, &ranges_in);
+}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [V11 4/8] drm/amd/pm: update driver_if and ppsmc headers for coming wbrf feature
  2023-08-31  6:20 ` Evan Quan
@ 2023-08-31  6:20   ` Evan Quan
  -1 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: netdev, linux-wireless, linux-kernel, dri-devel, linux-acpi,
	amd-gfx, Evan Quan

Add those data structures to support Wifi RFI mitigation feature.

Signed-off-by: Evan Quan <evan.quan@amd.com>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
---
 .../pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h | 14 +++++++++++++-
 .../pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_7.h | 14 +++++++++++++-
 .../amd/pm/swsmu/inc/pmfw_if/smu_v13_0_0_ppsmc.h   |  3 ++-
 .../amd/pm/swsmu/inc/pmfw_if/smu_v13_0_7_ppsmc.h   |  3 ++-
 4 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h
index 9dd1ed5b8940..e481407b6584 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h
@@ -391,6 +391,17 @@ typedef struct {
   EccInfo_t  EccInfo[24];
 } EccInfoTable_t;
 
+typedef struct {
+  uint16_t     LowFreq;
+  uint16_t     HighFreq;
+} WifiOneBand_t;
+
+typedef struct {
+  uint32_t         WifiBandEntryNum;
+  WifiOneBand_t    WifiBandEntry[11];
+  uint32_t         MmHubPadding[8];
+} WifiBandEntryTable_t;
+
 //D3HOT sequences
 typedef enum {
   BACO_SEQUENCE,
@@ -1615,7 +1626,8 @@ typedef struct {
 #define TABLE_I2C_COMMANDS            9
 #define TABLE_DRIVER_INFO             10
 #define TABLE_ECCINFO                 11
-#define TABLE_COUNT                   12
+#define TABLE_WIFIBAND                12
+#define TABLE_COUNT                   13
 
 //IH Interupt ID
 #define IH_INTERRUPT_ID_TO_DRIVER                   0xFE
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_7.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_7.h
index 62b7c0daff68..1530ca002c6c 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_7.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_7.h
@@ -392,6 +392,17 @@ typedef struct {
   EccInfo_t  EccInfo[24];
 } EccInfoTable_t;
 
+typedef struct {
+  uint16_t     LowFreq;
+  uint16_t     HighFreq;
+} WifiOneBand_t;
+
+typedef struct {
+  uint32_t         WifiBandEntryNum;
+  WifiOneBand_t    WifiBandEntry[11];
+  uint32_t         MmHubPadding[8];
+} WifiBandEntryTable_t;
+
 //D3HOT sequences
 typedef enum {
   BACO_SEQUENCE,
@@ -1605,7 +1616,8 @@ typedef struct {
 #define TABLE_I2C_COMMANDS            9
 #define TABLE_DRIVER_INFO             10
 #define TABLE_ECCINFO                 11
-#define TABLE_COUNT                   12
+#define TABLE_WIFIBAND                12
+#define TABLE_COUNT                   13
 
 //IH Interupt ID
 #define IH_INTERRUPT_ID_TO_DRIVER                   0xFE
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_0_ppsmc.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_0_ppsmc.h
index 10cff75b44d5..c98cc32d11bd 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_0_ppsmc.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_0_ppsmc.h
@@ -138,7 +138,8 @@
 #define PPSMC_MSG_SetBadMemoryPagesRetiredFlagsPerChannel 0x4A
 #define PPSMC_MSG_SetPriorityDeltaGain           0x4B
 #define PPSMC_MSG_AllowIHHostInterrupt           0x4C
-#define PPSMC_Message_Count                      0x4D
+#define PPSMC_MSG_EnableUCLKShadow               0x51
+#define PPSMC_Message_Count                      0x52
 
 //Debug Dump Message
 #define DEBUGSMC_MSG_TestMessage                    0x1
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_7_ppsmc.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_7_ppsmc.h
index 6aaefca9b595..a6bf9cdd130e 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_7_ppsmc.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_7_ppsmc.h
@@ -134,6 +134,7 @@
 #define PPSMC_MSG_SetBadMemoryPagesRetiredFlagsPerChannel 0x4A
 #define PPSMC_MSG_SetPriorityDeltaGain           0x4B
 #define PPSMC_MSG_AllowIHHostInterrupt           0x4C
-#define PPSMC_Message_Count                      0x4D
+#define PPSMC_MSG_EnableUCLKShadow               0x51
+#define PPSMC_Message_Count                      0x52
 
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [V11 4/8] drm/amd/pm: update driver_if and ppsmc headers for coming wbrf feature
@ 2023-08-31  6:20   ` Evan Quan
  0 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: linux-kernel, linux-acpi, amd-gfx, dri-devel, linux-wireless,
	netdev, Evan Quan

Add those data structures to support Wifi RFI mitigation feature.

Signed-off-by: Evan Quan <evan.quan@amd.com>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
---
 .../pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h | 14 +++++++++++++-
 .../pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_7.h | 14 +++++++++++++-
 .../amd/pm/swsmu/inc/pmfw_if/smu_v13_0_0_ppsmc.h   |  3 ++-
 .../amd/pm/swsmu/inc/pmfw_if/smu_v13_0_7_ppsmc.h   |  3 ++-
 4 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h
index 9dd1ed5b8940..e481407b6584 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h
@@ -391,6 +391,17 @@ typedef struct {
   EccInfo_t  EccInfo[24];
 } EccInfoTable_t;
 
+typedef struct {
+  uint16_t     LowFreq;
+  uint16_t     HighFreq;
+} WifiOneBand_t;
+
+typedef struct {
+  uint32_t         WifiBandEntryNum;
+  WifiOneBand_t    WifiBandEntry[11];
+  uint32_t         MmHubPadding[8];
+} WifiBandEntryTable_t;
+
 //D3HOT sequences
 typedef enum {
   BACO_SEQUENCE,
@@ -1615,7 +1626,8 @@ typedef struct {
 #define TABLE_I2C_COMMANDS            9
 #define TABLE_DRIVER_INFO             10
 #define TABLE_ECCINFO                 11
-#define TABLE_COUNT                   12
+#define TABLE_WIFIBAND                12
+#define TABLE_COUNT                   13
 
 //IH Interupt ID
 #define IH_INTERRUPT_ID_TO_DRIVER                   0xFE
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_7.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_7.h
index 62b7c0daff68..1530ca002c6c 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_7.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_7.h
@@ -392,6 +392,17 @@ typedef struct {
   EccInfo_t  EccInfo[24];
 } EccInfoTable_t;
 
+typedef struct {
+  uint16_t     LowFreq;
+  uint16_t     HighFreq;
+} WifiOneBand_t;
+
+typedef struct {
+  uint32_t         WifiBandEntryNum;
+  WifiOneBand_t    WifiBandEntry[11];
+  uint32_t         MmHubPadding[8];
+} WifiBandEntryTable_t;
+
 //D3HOT sequences
 typedef enum {
   BACO_SEQUENCE,
@@ -1605,7 +1616,8 @@ typedef struct {
 #define TABLE_I2C_COMMANDS            9
 #define TABLE_DRIVER_INFO             10
 #define TABLE_ECCINFO                 11
-#define TABLE_COUNT                   12
+#define TABLE_WIFIBAND                12
+#define TABLE_COUNT                   13
 
 //IH Interupt ID
 #define IH_INTERRUPT_ID_TO_DRIVER                   0xFE
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_0_ppsmc.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_0_ppsmc.h
index 10cff75b44d5..c98cc32d11bd 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_0_ppsmc.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_0_ppsmc.h
@@ -138,7 +138,8 @@
 #define PPSMC_MSG_SetBadMemoryPagesRetiredFlagsPerChannel 0x4A
 #define PPSMC_MSG_SetPriorityDeltaGain           0x4B
 #define PPSMC_MSG_AllowIHHostInterrupt           0x4C
-#define PPSMC_Message_Count                      0x4D
+#define PPSMC_MSG_EnableUCLKShadow               0x51
+#define PPSMC_Message_Count                      0x52
 
 //Debug Dump Message
 #define DEBUGSMC_MSG_TestMessage                    0x1
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_7_ppsmc.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_7_ppsmc.h
index 6aaefca9b595..a6bf9cdd130e 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_7_ppsmc.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_7_ppsmc.h
@@ -134,6 +134,7 @@
 #define PPSMC_MSG_SetBadMemoryPagesRetiredFlagsPerChannel 0x4A
 #define PPSMC_MSG_SetPriorityDeltaGain           0x4B
 #define PPSMC_MSG_AllowIHHostInterrupt           0x4C
-#define PPSMC_Message_Count                      0x4D
+#define PPSMC_MSG_EnableUCLKShadow               0x51
+#define PPSMC_Message_Count                      0x52
 
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [V11 5/8] drm/amd/pm: setup the framework to support Wifi RFI mitigation feature
  2023-08-31  6:20 ` Evan Quan
@ 2023-08-31  6:20   ` Evan Quan
  -1 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: netdev, linux-wireless, linux-kernel, dri-devel, linux-acpi,
	amd-gfx, Evan Quan

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 <evan.quan@amd.com>
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)
v9->v10:
v10->v11:
 - correct the logics for wbrf range sorting(Lijo)
---
 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     | 195 ++++++++++++++++++
 drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h |  23 +++
 drivers/gpu/drm/amd/pm/swsmu/smu_internal.h   |   3 +
 5 files changed, 240 insertions(+)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 97cb56c791f3..47595b892c1f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -246,6 +246,8 @@ extern int amdgpu_sg_display;
 
 extern int amdgpu_user_partt_mode;
 
+extern int amdgpu_wbrf;
+
 #define AMDGPU_VM_MAX_NUM_CTX			4096
 #define AMDGPU_SG_THRESHOLD			(256*1024*1024)
 #define AMDGPU_WAIT_IDLE_TIMEOUT_IN_MS	        3000
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 7691177d87aa..c7231ad89aa0 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -195,6 +195,7 @@ int amdgpu_use_xgmi_p2p = 1;
 int amdgpu_vcnfw_log;
 int amdgpu_sg_display = -1; /* auto */
 int amdgpu_user_partt_mode = AMDGPU_AUTO_COMPUTE_PARTITION_MODE;
+int amdgpu_wbrf = -1;
 
 static void amdgpu_drv_delayed_reset_work_handler(struct work_struct *work);
 
@@ -978,6 +979,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)
+ * 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
  */
diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
index 222af2fae745..bac2a362a2fc 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
@@ -1228,6 +1228,174 @@ static int smu_get_thermal_temperature_range(struct smu_context *smu)
 	return ret;
 }
 
+/**
+ * smu_wbrf_handle_exclusion_ranges - consume the wbrf exclusion ranges
+ *
+ * @smu: smu_context pointer
+ *
+ * Retrieve the wbrf exclusion ranges and send them to PMFW for proper handling.
+ * Returns 0 on success, error on failure.
+ */
+static int smu_wbrf_handle_exclusion_ranges(struct smu_context *smu)
+{
+	struct wbrf_ranges_in_out wbrf_exclusion = {0};
+	struct exclusion_range *wifi_bands = wbrf_exclusion.band_list;
+	struct amdgpu_device *adev = smu->adev;
+	uint32_t num_of_wbrf_ranges = MAX_NUM_OF_WBRF_RANGES;
+	uint64_t start, end;
+	int ret, i, j;
+
+	ret = acpi_amd_wbrf_retrieve_exclusions(adev->dev, &wbrf_exclusion);
+	if (ret) {
+		dev_err(adev->dev, "Failed to retrieve exclusion ranges!\n");
+		return ret;
+	}
+
+	/*
+	 * The exclusion ranges array we got might be filled with holes and duplicate
+	 * entries. For example:
+	 * {(2400, 2500), (0, 0), (6882, 6962), (2400, 2500), (0, 0), (6117, 6189), (0, 0)...}
+	 * We need to do some sortups to eliminate those holes and duplicate entries.
+	 * Expected output: {(2400, 2500), (6117, 6189), (6882, 6962), (0, 0)...}
+	 */
+	for (i = 0; i < num_of_wbrf_ranges; i++) {
+		start = wifi_bands[i].start;
+		end = wifi_bands[i].end;
+
+		/* get the last valid entry to fill the intermediate hole */
+		if (!start && !end) {
+			for (j = num_of_wbrf_ranges - 1; j > i; j--)
+				if (wifi_bands[j].start &&
+				    wifi_bands[j].end)
+					break;
+
+			/* no valid entry left */
+			if (j <= i)
+				break;
+
+			start = wifi_bands[i].start = wifi_bands[j].start;
+			end = wifi_bands[i].end = wifi_bands[j].end;
+			wifi_bands[j].start = 0;
+			wifi_bands[j].end = 0;
+			num_of_wbrf_ranges = j;
+		}
+
+		/* eliminate duplicate entries */
+		for (j = i + 1; j < num_of_wbrf_ranges; j++) {
+			if ((wifi_bands[j].start == start) &&
+			     (wifi_bands[j].end == end)) {
+				wifi_bands[j].start = 0;
+				wifi_bands[j].end = 0;
+			}
+		}
+	}
+
+	/* Send the sorted wifi_bands to PMFW */
+	ret = smu_set_wbrf_exclusion_ranges(smu, wifi_bands);
+	/* 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 &&
+			      acpi_amd_wbrf_supported_consumer(adev->dev);
+
+	if (smu->wbrf_supported)
+		dev_info(adev->dev, "RF interference mitigation is supported\n");
+}
+
+/**
+ * smu_wbrf_init - init driver wbrf support
+ *
+ * @smu: smu_context pointer
+ *
+ * Verifies the AMD ACPI interfaces and registers with the wbrf
+ * notifier chain if wbrf feature is supported.
+ * Returns 0 on success, error on failure.
+ */
+static int smu_wbrf_init(struct smu_context *smu)
+{
+	struct amdgpu_device *adev = smu->adev;
+	int ret;
+
+	if (!smu->wbrf_supported)
+		return 0;
+
+	smu->wbrf_notifier.notifier_call = smu_wbrf_event_handler;
+	ret = acpi_amd_wbrf_register_notifier(&smu->wbrf_notifier);
+	if (ret)
+		return ret;
+
+	/*
+	 * Some wifiband exclusion ranges may be already there
+	 * before our driver loaded. To make sure our driver
+	 * is awared of those exclusion ranges.
+	 */
+	ret = smu_wbrf_handle_exclusion_ranges(smu);
+	if (ret)
+		dev_err(adev->dev, "Failed to handle wbrf exclusion ranges\n");
+
+	return ret;
+}
+
+/**
+ * smu_wbrf_fini - tear down driver wbrf support
+ *
+ * @smu: smu_context pointer
+ *
+ * Unregisters with the wbrf notifier chain.
+ */
+static void smu_wbrf_fini(struct smu_context *smu)
+{
+	if (!smu->wbrf_supported)
+		return;
+
+	acpi_amd_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 +1488,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 +1593,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 +1657,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 +1816,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 6e2069dcb6b9..3eb1c72a76f1 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@ -22,6 +22,8 @@
 #ifndef __AMDGPU_SMU_H__
 #define __AMDGPU_SMU_H__
 
+#include <linux/acpi_amd_wbrf.h>
+
 #include "amdgpu.h"
 #include "kgd_pp_interface.h"
 #include "dm_pp_interface.h"
@@ -575,6 +577,10 @@ struct smu_context
 	u32 debug_resp_reg;
 
 	struct delayed_work		swctf_delayed_work;
+
+	/* data structures for wbrf feature support */
+	bool				wbrf_supported;
+	struct notifier_block		wbrf_notifier;
 };
 
 struct i2c_adapter;
@@ -1356,6 +1362,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


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [V11 5/8] drm/amd/pm: setup the framework to support Wifi RFI mitigation feature
@ 2023-08-31  6:20   ` Evan Quan
  0 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: linux-kernel, linux-acpi, amd-gfx, dri-devel, linux-wireless,
	netdev, Evan Quan

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 <evan.quan@amd.com>
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)
v9->v10:
v10->v11:
 - correct the logics for wbrf range sorting(Lijo)
---
 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     | 195 ++++++++++++++++++
 drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h |  23 +++
 drivers/gpu/drm/amd/pm/swsmu/smu_internal.h   |   3 +
 5 files changed, 240 insertions(+)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 97cb56c791f3..47595b892c1f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -246,6 +246,8 @@ extern int amdgpu_sg_display;
 
 extern int amdgpu_user_partt_mode;
 
+extern int amdgpu_wbrf;
+
 #define AMDGPU_VM_MAX_NUM_CTX			4096
 #define AMDGPU_SG_THRESHOLD			(256*1024*1024)
 #define AMDGPU_WAIT_IDLE_TIMEOUT_IN_MS	        3000
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 7691177d87aa..c7231ad89aa0 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -195,6 +195,7 @@ int amdgpu_use_xgmi_p2p = 1;
 int amdgpu_vcnfw_log;
 int amdgpu_sg_display = -1; /* auto */
 int amdgpu_user_partt_mode = AMDGPU_AUTO_COMPUTE_PARTITION_MODE;
+int amdgpu_wbrf = -1;
 
 static void amdgpu_drv_delayed_reset_work_handler(struct work_struct *work);
 
@@ -978,6 +979,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)
+ * 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
  */
diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
index 222af2fae745..bac2a362a2fc 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
@@ -1228,6 +1228,174 @@ static int smu_get_thermal_temperature_range(struct smu_context *smu)
 	return ret;
 }
 
+/**
+ * smu_wbrf_handle_exclusion_ranges - consume the wbrf exclusion ranges
+ *
+ * @smu: smu_context pointer
+ *
+ * Retrieve the wbrf exclusion ranges and send them to PMFW for proper handling.
+ * Returns 0 on success, error on failure.
+ */
+static int smu_wbrf_handle_exclusion_ranges(struct smu_context *smu)
+{
+	struct wbrf_ranges_in_out wbrf_exclusion = {0};
+	struct exclusion_range *wifi_bands = wbrf_exclusion.band_list;
+	struct amdgpu_device *adev = smu->adev;
+	uint32_t num_of_wbrf_ranges = MAX_NUM_OF_WBRF_RANGES;
+	uint64_t start, end;
+	int ret, i, j;
+
+	ret = acpi_amd_wbrf_retrieve_exclusions(adev->dev, &wbrf_exclusion);
+	if (ret) {
+		dev_err(adev->dev, "Failed to retrieve exclusion ranges!\n");
+		return ret;
+	}
+
+	/*
+	 * The exclusion ranges array we got might be filled with holes and duplicate
+	 * entries. For example:
+	 * {(2400, 2500), (0, 0), (6882, 6962), (2400, 2500), (0, 0), (6117, 6189), (0, 0)...}
+	 * We need to do some sortups to eliminate those holes and duplicate entries.
+	 * Expected output: {(2400, 2500), (6117, 6189), (6882, 6962), (0, 0)...}
+	 */
+	for (i = 0; i < num_of_wbrf_ranges; i++) {
+		start = wifi_bands[i].start;
+		end = wifi_bands[i].end;
+
+		/* get the last valid entry to fill the intermediate hole */
+		if (!start && !end) {
+			for (j = num_of_wbrf_ranges - 1; j > i; j--)
+				if (wifi_bands[j].start &&
+				    wifi_bands[j].end)
+					break;
+
+			/* no valid entry left */
+			if (j <= i)
+				break;
+
+			start = wifi_bands[i].start = wifi_bands[j].start;
+			end = wifi_bands[i].end = wifi_bands[j].end;
+			wifi_bands[j].start = 0;
+			wifi_bands[j].end = 0;
+			num_of_wbrf_ranges = j;
+		}
+
+		/* eliminate duplicate entries */
+		for (j = i + 1; j < num_of_wbrf_ranges; j++) {
+			if ((wifi_bands[j].start == start) &&
+			     (wifi_bands[j].end == end)) {
+				wifi_bands[j].start = 0;
+				wifi_bands[j].end = 0;
+			}
+		}
+	}
+
+	/* Send the sorted wifi_bands to PMFW */
+	ret = smu_set_wbrf_exclusion_ranges(smu, wifi_bands);
+	/* 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 &&
+			      acpi_amd_wbrf_supported_consumer(adev->dev);
+
+	if (smu->wbrf_supported)
+		dev_info(adev->dev, "RF interference mitigation is supported\n");
+}
+
+/**
+ * smu_wbrf_init - init driver wbrf support
+ *
+ * @smu: smu_context pointer
+ *
+ * Verifies the AMD ACPI interfaces and registers with the wbrf
+ * notifier chain if wbrf feature is supported.
+ * Returns 0 on success, error on failure.
+ */
+static int smu_wbrf_init(struct smu_context *smu)
+{
+	struct amdgpu_device *adev = smu->adev;
+	int ret;
+
+	if (!smu->wbrf_supported)
+		return 0;
+
+	smu->wbrf_notifier.notifier_call = smu_wbrf_event_handler;
+	ret = acpi_amd_wbrf_register_notifier(&smu->wbrf_notifier);
+	if (ret)
+		return ret;
+
+	/*
+	 * Some wifiband exclusion ranges may be already there
+	 * before our driver loaded. To make sure our driver
+	 * is awared of those exclusion ranges.
+	 */
+	ret = smu_wbrf_handle_exclusion_ranges(smu);
+	if (ret)
+		dev_err(adev->dev, "Failed to handle wbrf exclusion ranges\n");
+
+	return ret;
+}
+
+/**
+ * smu_wbrf_fini - tear down driver wbrf support
+ *
+ * @smu: smu_context pointer
+ *
+ * Unregisters with the wbrf notifier chain.
+ */
+static void smu_wbrf_fini(struct smu_context *smu)
+{
+	if (!smu->wbrf_supported)
+		return;
+
+	acpi_amd_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 +1488,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 +1593,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 +1657,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 +1816,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 6e2069dcb6b9..3eb1c72a76f1 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@ -22,6 +22,8 @@
 #ifndef __AMDGPU_SMU_H__
 #define __AMDGPU_SMU_H__
 
+#include <linux/acpi_amd_wbrf.h>
+
 #include "amdgpu.h"
 #include "kgd_pp_interface.h"
 #include "dm_pp_interface.h"
@@ -575,6 +577,10 @@ struct smu_context
 	u32 debug_resp_reg;
 
 	struct delayed_work		swctf_delayed_work;
+
+	/* data structures for wbrf feature support */
+	bool				wbrf_supported;
+	struct notifier_block		wbrf_notifier;
 };
 
 struct i2c_adapter;
@@ -1356,6 +1362,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


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [V11 6/8] drm/amd/pm: add flood detection for wbrf events
  2023-08-31  6:20 ` Evan Quan
@ 2023-08-31  6:20   ` Evan Quan
  -1 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: netdev, linux-wireless, linux-kernel, dri-devel, linux-acpi,
	amd-gfx, Evan Quan

To protect PMFW from being overloaded.

Signed-off-by: Evan Quan <evan.quan@amd.com>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
---
 drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c     | 31 +++++++++++++++----
 drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h |  7 +++++
 2 files changed, 32 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
index bac2a362a2fc..2dbdb4149428 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
@@ -1319,7 +1319,8 @@ static int smu_wbrf_event_handler(struct notifier_block *nb,
 
 	switch (action) {
 	case WBRF_CHANGED:
-		smu_wbrf_handle_exclusion_ranges(smu);
+		schedule_delayed_work(&smu->wbrf_delayed_work,
+				      msecs_to_jiffies(SMU_WBRF_EVENT_HANDLING_PACE));
 		break;
 	default:
 		return NOTIFY_DONE;
@@ -1328,6 +1329,21 @@ static int smu_wbrf_event_handler(struct notifier_block *nb,
 	return NOTIFY_OK;
 }
 
+/**
+ * smu_wbrf_delayed_work_handler - callback on delayed work timer expired
+ *
+ * @work: struct work_struct pointer
+ *
+ * Flood is over and driver will consume the latest exclusion ranges.
+ */
+static void smu_wbrf_delayed_work_handler(struct work_struct *work)
+{
+	struct smu_context *smu =
+		container_of(work, struct smu_context, wbrf_delayed_work.work);
+
+	smu_wbrf_handle_exclusion_ranges(smu);
+}
+
 /**
  * smu_wbrf_support_check - check wbrf support
  *
@@ -1358,12 +1374,14 @@ static void smu_wbrf_support_check(struct smu_context *smu)
  */
 static int smu_wbrf_init(struct smu_context *smu)
 {
-	struct amdgpu_device *adev = smu->adev;
 	int ret;
 
 	if (!smu->wbrf_supported)
 		return 0;
 
+	INIT_DELAYED_WORK(&smu->wbrf_delayed_work,
+			  smu_wbrf_delayed_work_handler);
+
 	smu->wbrf_notifier.notifier_call = smu_wbrf_event_handler;
 	ret = acpi_amd_wbrf_register_notifier(&smu->wbrf_notifier);
 	if (ret)
@@ -1374,11 +1392,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;
 }
 
 /**
@@ -1394,6 +1411,8 @@ static void smu_wbrf_fini(struct smu_context *smu)
 		return;
 
 	acpi_amd_wbrf_unregister_notifier(&smu->wbrf_notifier);
+
+	cancel_delayed_work_sync(&smu->wbrf_delayed_work);
 }
 
 static int smu_smc_hw_setup(struct smu_context *smu)
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 3eb1c72a76f1..60d595344c45 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@ -480,6 +480,12 @@ struct stb_context {
 
 #define WORKLOAD_POLICY_MAX 7
 
+/*
+ * Configure wbrf event handling pace as there can be only one
+ * event processed every SMU_WBRF_EVENT_HANDLING_PACE ms.
+ */
+#define SMU_WBRF_EVENT_HANDLING_PACE	10
+
 struct smu_context
 {
 	struct amdgpu_device            *adev;
@@ -581,6 +587,7 @@ struct smu_context
 	/* data structures for wbrf feature support */
 	bool				wbrf_supported;
 	struct notifier_block		wbrf_notifier;
+	struct delayed_work		wbrf_delayed_work;
 };
 
 struct i2c_adapter;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [V11 6/8] drm/amd/pm: add flood detection for wbrf events
@ 2023-08-31  6:20   ` Evan Quan
  0 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: linux-kernel, linux-acpi, amd-gfx, dri-devel, linux-wireless,
	netdev, Evan Quan

To protect PMFW from being overloaded.

Signed-off-by: Evan Quan <evan.quan@amd.com>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
---
 drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c     | 31 +++++++++++++++----
 drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h |  7 +++++
 2 files changed, 32 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
index bac2a362a2fc..2dbdb4149428 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
@@ -1319,7 +1319,8 @@ static int smu_wbrf_event_handler(struct notifier_block *nb,
 
 	switch (action) {
 	case WBRF_CHANGED:
-		smu_wbrf_handle_exclusion_ranges(smu);
+		schedule_delayed_work(&smu->wbrf_delayed_work,
+				      msecs_to_jiffies(SMU_WBRF_EVENT_HANDLING_PACE));
 		break;
 	default:
 		return NOTIFY_DONE;
@@ -1328,6 +1329,21 @@ static int smu_wbrf_event_handler(struct notifier_block *nb,
 	return NOTIFY_OK;
 }
 
+/**
+ * smu_wbrf_delayed_work_handler - callback on delayed work timer expired
+ *
+ * @work: struct work_struct pointer
+ *
+ * Flood is over and driver will consume the latest exclusion ranges.
+ */
+static void smu_wbrf_delayed_work_handler(struct work_struct *work)
+{
+	struct smu_context *smu =
+		container_of(work, struct smu_context, wbrf_delayed_work.work);
+
+	smu_wbrf_handle_exclusion_ranges(smu);
+}
+
 /**
  * smu_wbrf_support_check - check wbrf support
  *
@@ -1358,12 +1374,14 @@ static void smu_wbrf_support_check(struct smu_context *smu)
  */
 static int smu_wbrf_init(struct smu_context *smu)
 {
-	struct amdgpu_device *adev = smu->adev;
 	int ret;
 
 	if (!smu->wbrf_supported)
 		return 0;
 
+	INIT_DELAYED_WORK(&smu->wbrf_delayed_work,
+			  smu_wbrf_delayed_work_handler);
+
 	smu->wbrf_notifier.notifier_call = smu_wbrf_event_handler;
 	ret = acpi_amd_wbrf_register_notifier(&smu->wbrf_notifier);
 	if (ret)
@@ -1374,11 +1392,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;
 }
 
 /**
@@ -1394,6 +1411,8 @@ static void smu_wbrf_fini(struct smu_context *smu)
 		return;
 
 	acpi_amd_wbrf_unregister_notifier(&smu->wbrf_notifier);
+
+	cancel_delayed_work_sync(&smu->wbrf_delayed_work);
 }
 
 static int smu_smc_hw_setup(struct smu_context *smu)
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 3eb1c72a76f1..60d595344c45 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@ -480,6 +480,12 @@ struct stb_context {
 
 #define WORKLOAD_POLICY_MAX 7
 
+/*
+ * Configure wbrf event handling pace as there can be only one
+ * event processed every SMU_WBRF_EVENT_HANDLING_PACE ms.
+ */
+#define SMU_WBRF_EVENT_HANDLING_PACE	10
+
 struct smu_context
 {
 	struct amdgpu_device            *adev;
@@ -581,6 +587,7 @@ struct smu_context
 	/* data structures for wbrf feature support */
 	bool				wbrf_supported;
 	struct notifier_block		wbrf_notifier;
+	struct delayed_work		wbrf_delayed_work;
 };
 
 struct i2c_adapter;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [V11 7/8] drm/amd/pm: enable Wifi RFI mitigation feature support for SMU13.0.0
  2023-08-31  6:20 ` Evan Quan
@ 2023-08-31  6:20   ` Evan Quan
  -1 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: netdev, linux-wireless, linux-kernel, dri-devel, linux-acpi,
	amd-gfx, Evan Quan

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

Signed-off-by: Evan Quan <evan.quan@amd.com>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
--
v10->v11:
  - downgrade the prompt level on message failure(Lijo)
---
 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 60d595344c45..a081e6bb27c4 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@ -325,6 +325,7 @@ enum smu_table_id
 	SMU_TABLE_PACE,
 	SMU_TABLE_ECCINFO,
 	SMU_TABLE_COMBO_PPTABLE,
+	SMU_TABLE_WIFIBAND,
 	SMU_TABLE_COUNT,
 };
 
@@ -1501,6 +1502,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 355c156d871a..dd70b56aa71e 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
@@ -299,5 +299,8 @@ int smu_v13_0_update_pcie_parameters(struct smu_context *smu,
 				     uint32_t pcie_gen_cap,
 				     uint32_t pcie_width_cap);
 
+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 9b62b45ebb7f..6a5cb582aa92 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
@@ -2472,3 +2472,12 @@ int smu_v13_0_update_pcie_parameters(struct smu_context *smu,
 
 	return 0;
 }
+
+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 fddcd834bcec..5a7a5804794a 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
@@ -154,6 +154,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] = {
@@ -237,6 +238,7 @@ static struct cmn2asic_mapping smu_v13_0_0_table_map[SMU_TABLE_COUNT] = {
 	TAB_MAP(I2C_COMMANDS),
 	TAB_MAP(ECCINFO),
 	TAB_MAP(OVERDRIVE),
+	TAB_MAP(WIFIBAND),
 };
 
 static struct cmn2asic_mapping smu_v13_0_0_pwr_src_map[SMU_POWER_SOURCE_COUNT] = {
@@ -492,6 +494,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)
@@ -2603,6 +2608,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]) {
+	case IP_VERSION(13, 0, 0):
+		return smu->smc_fw_version >= 0x004e6300;
+	case IP_VERSION(13, 0, 10):
+		return smu->smc_fw_version >= 0x00503300;
+	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_warn(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,
@@ -2682,6 +2739,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


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [V11 7/8] drm/amd/pm: enable Wifi RFI mitigation feature support for SMU13.0.0
@ 2023-08-31  6:20   ` Evan Quan
  0 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: linux-kernel, linux-acpi, amd-gfx, dri-devel, linux-wireless,
	netdev, Evan Quan

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

Signed-off-by: Evan Quan <evan.quan@amd.com>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
--
v10->v11:
  - downgrade the prompt level on message failure(Lijo)
---
 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 60d595344c45..a081e6bb27c4 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@ -325,6 +325,7 @@ enum smu_table_id
 	SMU_TABLE_PACE,
 	SMU_TABLE_ECCINFO,
 	SMU_TABLE_COMBO_PPTABLE,
+	SMU_TABLE_WIFIBAND,
 	SMU_TABLE_COUNT,
 };
 
@@ -1501,6 +1502,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 355c156d871a..dd70b56aa71e 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
@@ -299,5 +299,8 @@ int smu_v13_0_update_pcie_parameters(struct smu_context *smu,
 				     uint32_t pcie_gen_cap,
 				     uint32_t pcie_width_cap);
 
+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 9b62b45ebb7f..6a5cb582aa92 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
@@ -2472,3 +2472,12 @@ int smu_v13_0_update_pcie_parameters(struct smu_context *smu,
 
 	return 0;
 }
+
+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 fddcd834bcec..5a7a5804794a 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
@@ -154,6 +154,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] = {
@@ -237,6 +238,7 @@ static struct cmn2asic_mapping smu_v13_0_0_table_map[SMU_TABLE_COUNT] = {
 	TAB_MAP(I2C_COMMANDS),
 	TAB_MAP(ECCINFO),
 	TAB_MAP(OVERDRIVE),
+	TAB_MAP(WIFIBAND),
 };
 
 static struct cmn2asic_mapping smu_v13_0_0_pwr_src_map[SMU_POWER_SOURCE_COUNT] = {
@@ -492,6 +494,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)
@@ -2603,6 +2608,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]) {
+	case IP_VERSION(13, 0, 0):
+		return smu->smc_fw_version >= 0x004e6300;
+	case IP_VERSION(13, 0, 10):
+		return smu->smc_fw_version >= 0x00503300;
+	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_warn(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,
@@ -2682,6 +2739,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


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [V11 8/8] drm/amd/pm: enable Wifi RFI mitigation feature support for SMU13.0.7
  2023-08-31  6:20 ` Evan Quan
@ 2023-08-31  6:20   ` Evan Quan
  -1 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: netdev, linux-wireless, linux-kernel, dri-devel, linux-acpi,
	amd-gfx, Evan Quan

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

Signed-off-by: Evan Quan <evan.quan@amd.com>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
--
v10->v11:
  - downgrade the prompt level on message failure(Lijo)
---
 .../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 62f2886ab4df..d1a93f961e9e 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
@@ -126,6 +126,7 @@ static struct cmn2asic_msg_mapping smu_v13_0_7_message_map[SMU_MSG_MAX_COUNT] =
 	MSG_MAP(AllowGpo,			PPSMC_MSG_SetGpoAllow,           0),
 	MSG_MAP(GetPptLimit,			PPSMC_MSG_GetPptLimit,                 0),
 	MSG_MAP(NotifyPowerSource,		PPSMC_MSG_NotifyPowerSource,           0),
+	MSG_MAP(EnableUCLKShadow,		PPSMC_MSG_EnableUCLKShadow,            0),
 };
 
 static struct cmn2asic_mapping smu_v13_0_7_clk_map[SMU_CLK_COUNT] = {
@@ -207,6 +208,7 @@ static struct cmn2asic_mapping smu_v13_0_7_table_map[SMU_TABLE_COUNT] = {
 	TAB_MAP(ACTIVITY_MONITOR_COEFF),
 	[SMU_TABLE_COMBO_PPTABLE] = {1, TABLE_COMBO_PPTABLE},
 	TAB_MAP(OVERDRIVE),
+	TAB_MAP(WIFIBAND),
 };
 
 static struct cmn2asic_mapping smu_v13_0_7_pwr_src_map[SMU_POWER_SOURCE_COUNT] = {
@@ -503,6 +505,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)
@@ -2179,6 +2184,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_warn(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,
@@ -2247,6 +2303,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


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [V11 8/8] drm/amd/pm: enable Wifi RFI mitigation feature support for SMU13.0.7
@ 2023-08-31  6:20   ` Evan Quan
  0 siblings, 0 replies; 26+ messages in thread
From: Evan Quan @ 2023-08-31  6:20 UTC (permalink / raw)
  To: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello
  Cc: linux-kernel, linux-acpi, amd-gfx, dri-devel, linux-wireless,
	netdev, Evan Quan

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

Signed-off-by: Evan Quan <evan.quan@amd.com>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
--
v10->v11:
  - downgrade the prompt level on message failure(Lijo)
---
 .../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 62f2886ab4df..d1a93f961e9e 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
@@ -126,6 +126,7 @@ static struct cmn2asic_msg_mapping smu_v13_0_7_message_map[SMU_MSG_MAX_COUNT] =
 	MSG_MAP(AllowGpo,			PPSMC_MSG_SetGpoAllow,           0),
 	MSG_MAP(GetPptLimit,			PPSMC_MSG_GetPptLimit,                 0),
 	MSG_MAP(NotifyPowerSource,		PPSMC_MSG_NotifyPowerSource,           0),
+	MSG_MAP(EnableUCLKShadow,		PPSMC_MSG_EnableUCLKShadow,            0),
 };
 
 static struct cmn2asic_mapping smu_v13_0_7_clk_map[SMU_CLK_COUNT] = {
@@ -207,6 +208,7 @@ static struct cmn2asic_mapping smu_v13_0_7_table_map[SMU_TABLE_COUNT] = {
 	TAB_MAP(ACTIVITY_MONITOR_COEFF),
 	[SMU_TABLE_COMBO_PPTABLE] = {1, TABLE_COMBO_PPTABLE},
 	TAB_MAP(OVERDRIVE),
+	TAB_MAP(WIFIBAND),
 };
 
 static struct cmn2asic_mapping smu_v13_0_7_pwr_src_map[SMU_POWER_SOURCE_COUNT] = {
@@ -503,6 +505,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)
@@ -2179,6 +2184,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_warn(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,
@@ -2247,6 +2303,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


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [V11 3/8] wifi: mac80211: Add support for WBRF features
  2023-08-31  6:20   ` Evan Quan
@ 2023-09-01 14:32     ` Jeff Johnson
  -1 siblings, 0 replies; 26+ messages in thread
From: Jeff Johnson @ 2023-09-01 14:32 UTC (permalink / raw)
  To: Evan Quan, lenb, johannes, davem, edumazet, kuba, pabeni,
	alexander.deucher, rafael, Lijo.Lazar, mario.limonciello
  Cc: linux-kernel, linux-acpi, amd-gfx, dri-devel, linux-wireless, netdev

On 8/30/2023 11:20 PM, Evan Quan wrote:
> To support the WBRF mechanism, Wifi adapters utilized in the system must

Since this is the first mention of WBRF in the core wireless code IMO 
you should indicate what this is an acronym for and briefly describe it
(or add a lore link).

I'm wondering if WBRF is just a special case of frequency avoidance, and 
that more generic naming/terminology should be used in core wireless.
For example, I know there are vendor-specific solutions which allow 
Wi-Fi to avoid using channels which may conflict with cellular or 
BlueTooth, and those may benefit from a more generic

> 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 <evan.quan@amd.com>
> Signed-off-by: Evan Quan <evan.quan@amd.com>
> --
> 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)
> v9->v10:
>    - get ranges_in->num_of_ranges set and passed in(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        | 105 +++++++++++++++++++++++++++++++++++++
>   6 files changed, 128 insertions(+)
>   create mode 100644 net/mac80211/wbrf.c
> 
> diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
> index 4b998090898e..f995d06da87f 100644
> --- a/include/linux/ieee80211.h
> +++ b/include/linux/ieee80211.h
> @@ -4335,6 +4335,7 @@ static inline int ieee80211_get_tdls_action(struct sk_buff *skb, u32 hdr_size)
>   /* convert frequencies */
>   #define MHZ_TO_KHZ(freq) ((freq) * 1000)
>   #define KHZ_TO_MHZ(freq) ((freq) / 1000)
> +#define KHZ_TO_HZ(freq)  ((freq) * 1000)
>   #define PR_KHZ(f) KHZ_TO_MHZ(f), f % 1000
>   #define KHZ_F "%d.%03d"
>   
> diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
> index b8de44da1fb8..d46c36f55fd3 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-y += wbrf.o
> +
>   ccflags-y += -DDEBUG
> diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
> index 68952752b599..458469c224ae 100644
> --- a/net/mac80211/chan.c
> +++ b/net/mac80211/chan.c
> @@ -506,11 +506,16 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local,
>   
>   	WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
>   
> +	ieee80211_remove_wbrf(local, &ctx->conf.def);
> +
>   	ctx->conf.def = *chandef;
>   
>   	/* check if min chanctx also changed */
>   	changed = IEEE80211_CHANCTX_CHANGE_WIDTH |
>   		  _ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for);
> +
> +	ieee80211_add_wbrf(local, &ctx->conf.def);
> +
>   	drv_change_chanctx(local, ctx, changed);
>   
>   	if (!local->use_chanctx) {
> @@ -668,6 +673,8 @@ static int ieee80211_add_chanctx(struct ieee80211_local *local,
>   	lockdep_assert_held(&local->mtx);
>   	lockdep_assert_held(&local->chanctx_mtx);
>   
> +	ieee80211_add_wbrf(local, &ctx->conf.def);
> +
>   	if (!local->use_chanctx)
>   		local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
>   
> @@ -748,6 +755,8 @@ static void ieee80211_del_chanctx(struct ieee80211_local *local,
>   	}
>   
>   	ieee80211_recalc_idle(local);
> +
> +	ieee80211_remove_wbrf(local, &ctx->conf.def);
>   }
>   
>   static void ieee80211_free_chanctx(struct ieee80211_local *local,
> diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
> index 91633a0b723e..719f2c892132 100644
> --- a/net/mac80211/ieee80211_i.h
> +++ b/net/mac80211/ieee80211_i.h
> @@ -1600,6 +1600,8 @@ struct ieee80211_local {
>   
>   	/* extended capabilities provided by mac80211 */
>   	u8 ext_capa[8];
> +
> +	bool wbrf_supported;
>   };
>   
>   static inline struct ieee80211_sub_if_data *
> @@ -2638,4 +2640,11 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata,
>   				    const struct ieee80211_eht_cap_elem *eht_cap_ie_elem,
>   				    u8 eht_cap_len,
>   				    struct link_sta_info *link_sta);
> +
> +void ieee80211_check_wbrf_support(struct ieee80211_local *local);
> +void ieee80211_add_wbrf(struct ieee80211_local *local,
> +			struct cfg80211_chan_def *chandef);
> +void ieee80211_remove_wbrf(struct ieee80211_local *local,
> +			   struct cfg80211_chan_def *chandef);
> +
>   #endif /* IEEE80211_I_H */
> diff --git a/net/mac80211/main.c b/net/mac80211/main.c
> index 24315d7b3126..b20bdaac84db 100644
> --- a/net/mac80211/main.c
> +++ b/net/mac80211/main.c
> @@ -1396,6 +1396,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
>   	debugfs_hw_add(local);
>   	rate_control_add_debugfs(local);
>   
> +	ieee80211_check_wbrf_support(local);
> +
>   	rtnl_lock();
>   	wiphy_lock(hw->wiphy);
>   
> diff --git a/net/mac80211/wbrf.c b/net/mac80211/wbrf.c
> new file mode 100644
> index 000000000000..63978c7d2bcb
> --- /dev/null
> +++ b/net/mac80211/wbrf.c
> @@ -0,0 +1,105 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Wifi Band Exclusion Interface for WWAN
> + * Copyright (C) 2023 Advanced Micro Devices
> + *
> + */
> +
> +#include <linux/acpi_amd_wbrf.h>
> +#include <net/cfg80211.h>
> +#include "ieee80211_i.h"
> +
> +void ieee80211_check_wbrf_support(struct ieee80211_local *local)
> +{
> +	struct wiphy *wiphy = local->hw.wiphy;
> +	struct device *dev;
> +
> +	if (!wiphy)
> +		return;
> +
> +	dev = wiphy->dev.parent;
> +	if (!dev)
> +		return;
> +
> +	local->wbrf_supported = acpi_amd_wbrf_supported_producer(dev);


I haven't been looking at this series closely so perhaps this has 
already been answered, but is this layered correctly? I'm surprised to 
see core wireless code directly invoking a vendor-specific API.
Instead should there be a registration mechanism with associated callbacks?

As I mentioned above I could envision multiple registrants for such an 
interface.

> +	dev_dbg(dev, "WBRF is %s supported\n",
> +		local->wbrf_supported ? "" : "not");
> +}
> +
> +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 void get_ranges_from_chandef(struct cfg80211_chan_def *chandef,
> +				    struct wbrf_ranges_in_out *ranges_in)
> +{
> +	u64 start_freq1, end_freq1;
> +	u64 start_freq2, end_freq2;
> +	int bandwidth;
> +
> +	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;
> +	ranges_in->num_of_ranges = 1;
> +
> +	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;
> +		ranges_in->num_of_ranges++;
> +	}
> +}
> +
> +void ieee80211_add_wbrf(struct ieee80211_local *local,
> +			struct cfg80211_chan_def *chandef)
> +{
> +	struct wbrf_ranges_in_out ranges_in = {0};
> +	struct device *dev;
> +
> +	if (!local->wbrf_supported)
> +		return;
> +
> +	dev = local->hw.wiphy->dev.parent;
> +
> +	get_ranges_from_chandef(chandef, &ranges_in);
> +
> +	acpi_amd_wbrf_add_exclusion(dev, &ranges_in);
> +}
> +
> +void ieee80211_remove_wbrf(struct ieee80211_local *local,
> +			   struct cfg80211_chan_def *chandef)
> +{
> +	struct wbrf_ranges_in_out ranges_in = {0};
> +	struct device *dev;
> +
> +	if (!local->wbrf_supported)
> +		return;
> +
> +	dev = local->hw.wiphy->dev.parent;
> +
> +	get_ranges_from_chandef(chandef, &ranges_in);
> +
> +	acpi_amd_wbrf_remove_exclusion(dev, &ranges_in);
> +}


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [V11 3/8] wifi: mac80211: Add support for WBRF features
@ 2023-09-01 14:32     ` Jeff Johnson
  0 siblings, 0 replies; 26+ messages in thread
From: Jeff Johnson @ 2023-09-01 14:32 UTC (permalink / raw)
  To: Evan Quan, lenb, johannes, davem, edumazet, kuba, pabeni,
	alexander.deucher, rafael, Lijo.Lazar, mario.limonciello
  Cc: netdev, linux-wireless, linux-kernel, dri-devel, linux-acpi, amd-gfx

On 8/30/2023 11:20 PM, Evan Quan wrote:
> To support the WBRF mechanism, Wifi adapters utilized in the system must

Since this is the first mention of WBRF in the core wireless code IMO 
you should indicate what this is an acronym for and briefly describe it
(or add a lore link).

I'm wondering if WBRF is just a special case of frequency avoidance, and 
that more generic naming/terminology should be used in core wireless.
For example, I know there are vendor-specific solutions which allow 
Wi-Fi to avoid using channels which may conflict with cellular or 
BlueTooth, and those may benefit from a more generic

> 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 <evan.quan@amd.com>
> Signed-off-by: Evan Quan <evan.quan@amd.com>
> --
> 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)
> v9->v10:
>    - get ranges_in->num_of_ranges set and passed in(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        | 105 +++++++++++++++++++++++++++++++++++++
>   6 files changed, 128 insertions(+)
>   create mode 100644 net/mac80211/wbrf.c
> 
> diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
> index 4b998090898e..f995d06da87f 100644
> --- a/include/linux/ieee80211.h
> +++ b/include/linux/ieee80211.h
> @@ -4335,6 +4335,7 @@ static inline int ieee80211_get_tdls_action(struct sk_buff *skb, u32 hdr_size)
>   /* convert frequencies */
>   #define MHZ_TO_KHZ(freq) ((freq) * 1000)
>   #define KHZ_TO_MHZ(freq) ((freq) / 1000)
> +#define KHZ_TO_HZ(freq)  ((freq) * 1000)
>   #define PR_KHZ(f) KHZ_TO_MHZ(f), f % 1000
>   #define KHZ_F "%d.%03d"
>   
> diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
> index b8de44da1fb8..d46c36f55fd3 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-y += wbrf.o
> +
>   ccflags-y += -DDEBUG
> diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
> index 68952752b599..458469c224ae 100644
> --- a/net/mac80211/chan.c
> +++ b/net/mac80211/chan.c
> @@ -506,11 +506,16 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local,
>   
>   	WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
>   
> +	ieee80211_remove_wbrf(local, &ctx->conf.def);
> +
>   	ctx->conf.def = *chandef;
>   
>   	/* check if min chanctx also changed */
>   	changed = IEEE80211_CHANCTX_CHANGE_WIDTH |
>   		  _ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for);
> +
> +	ieee80211_add_wbrf(local, &ctx->conf.def);
> +
>   	drv_change_chanctx(local, ctx, changed);
>   
>   	if (!local->use_chanctx) {
> @@ -668,6 +673,8 @@ static int ieee80211_add_chanctx(struct ieee80211_local *local,
>   	lockdep_assert_held(&local->mtx);
>   	lockdep_assert_held(&local->chanctx_mtx);
>   
> +	ieee80211_add_wbrf(local, &ctx->conf.def);
> +
>   	if (!local->use_chanctx)
>   		local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
>   
> @@ -748,6 +755,8 @@ static void ieee80211_del_chanctx(struct ieee80211_local *local,
>   	}
>   
>   	ieee80211_recalc_idle(local);
> +
> +	ieee80211_remove_wbrf(local, &ctx->conf.def);
>   }
>   
>   static void ieee80211_free_chanctx(struct ieee80211_local *local,
> diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
> index 91633a0b723e..719f2c892132 100644
> --- a/net/mac80211/ieee80211_i.h
> +++ b/net/mac80211/ieee80211_i.h
> @@ -1600,6 +1600,8 @@ struct ieee80211_local {
>   
>   	/* extended capabilities provided by mac80211 */
>   	u8 ext_capa[8];
> +
> +	bool wbrf_supported;
>   };
>   
>   static inline struct ieee80211_sub_if_data *
> @@ -2638,4 +2640,11 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata,
>   				    const struct ieee80211_eht_cap_elem *eht_cap_ie_elem,
>   				    u8 eht_cap_len,
>   				    struct link_sta_info *link_sta);
> +
> +void ieee80211_check_wbrf_support(struct ieee80211_local *local);
> +void ieee80211_add_wbrf(struct ieee80211_local *local,
> +			struct cfg80211_chan_def *chandef);
> +void ieee80211_remove_wbrf(struct ieee80211_local *local,
> +			   struct cfg80211_chan_def *chandef);
> +
>   #endif /* IEEE80211_I_H */
> diff --git a/net/mac80211/main.c b/net/mac80211/main.c
> index 24315d7b3126..b20bdaac84db 100644
> --- a/net/mac80211/main.c
> +++ b/net/mac80211/main.c
> @@ -1396,6 +1396,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
>   	debugfs_hw_add(local);
>   	rate_control_add_debugfs(local);
>   
> +	ieee80211_check_wbrf_support(local);
> +
>   	rtnl_lock();
>   	wiphy_lock(hw->wiphy);
>   
> diff --git a/net/mac80211/wbrf.c b/net/mac80211/wbrf.c
> new file mode 100644
> index 000000000000..63978c7d2bcb
> --- /dev/null
> +++ b/net/mac80211/wbrf.c
> @@ -0,0 +1,105 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Wifi Band Exclusion Interface for WWAN
> + * Copyright (C) 2023 Advanced Micro Devices
> + *
> + */
> +
> +#include <linux/acpi_amd_wbrf.h>
> +#include <net/cfg80211.h>
> +#include "ieee80211_i.h"
> +
> +void ieee80211_check_wbrf_support(struct ieee80211_local *local)
> +{
> +	struct wiphy *wiphy = local->hw.wiphy;
> +	struct device *dev;
> +
> +	if (!wiphy)
> +		return;
> +
> +	dev = wiphy->dev.parent;
> +	if (!dev)
> +		return;
> +
> +	local->wbrf_supported = acpi_amd_wbrf_supported_producer(dev);


I haven't been looking at this series closely so perhaps this has 
already been answered, but is this layered correctly? I'm surprised to 
see core wireless code directly invoking a vendor-specific API.
Instead should there be a registration mechanism with associated callbacks?

As I mentioned above I could envision multiple registrants for such an 
interface.

> +	dev_dbg(dev, "WBRF is %s supported\n",
> +		local->wbrf_supported ? "" : "not");
> +}
> +
> +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 void get_ranges_from_chandef(struct cfg80211_chan_def *chandef,
> +				    struct wbrf_ranges_in_out *ranges_in)
> +{
> +	u64 start_freq1, end_freq1;
> +	u64 start_freq2, end_freq2;
> +	int bandwidth;
> +
> +	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;
> +	ranges_in->num_of_ranges = 1;
> +
> +	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;
> +		ranges_in->num_of_ranges++;
> +	}
> +}
> +
> +void ieee80211_add_wbrf(struct ieee80211_local *local,
> +			struct cfg80211_chan_def *chandef)
> +{
> +	struct wbrf_ranges_in_out ranges_in = {0};
> +	struct device *dev;
> +
> +	if (!local->wbrf_supported)
> +		return;
> +
> +	dev = local->hw.wiphy->dev.parent;
> +
> +	get_ranges_from_chandef(chandef, &ranges_in);
> +
> +	acpi_amd_wbrf_add_exclusion(dev, &ranges_in);
> +}
> +
> +void ieee80211_remove_wbrf(struct ieee80211_local *local,
> +			   struct cfg80211_chan_def *chandef)
> +{
> +	struct wbrf_ranges_in_out ranges_in = {0};
> +	struct device *dev;
> +
> +	if (!local->wbrf_supported)
> +		return;
> +
> +	dev = local->hw.wiphy->dev.parent;
> +
> +	get_ranges_from_chandef(chandef, &ranges_in);
> +
> +	acpi_amd_wbrf_remove_exclusion(dev, &ranges_in);
> +}


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [V11 3/8] wifi: mac80211: Add support for WBRF features
  2023-09-01 14:32     ` Jeff Johnson
@ 2023-09-06 19:57       ` Mario Limonciello
  -1 siblings, 0 replies; 26+ messages in thread
From: Mario Limonciello @ 2023-09-06 19:57 UTC (permalink / raw)
  To: Jeff Johnson, Evan Quan, lenb, johannes, davem, edumazet, kuba,
	pabeni, alexander.deucher, rafael, Lijo.Lazar
  Cc: linux-kernel, linux-acpi, amd-gfx, dri-devel, linux-wireless, netdev

On 9/1/2023 09:32, Jeff Johnson wrote:
> On 8/30/2023 11:20 PM, Evan Quan wrote:
>> To support the WBRF mechanism, Wifi adapters utilized in the system must
> 
> Since this is the first mention of WBRF in the core wireless code IMO 
> you should indicate what this is an acronym for and briefly describe it
> (or add a lore link).

A lot of information is captured in the cover letter and earlier 
commits.  I think you raise a good point that 10 years from now someone 
looking at random commits will have a hard time understanding what 
exactly WBRF stands for.

How about if we introduce a wbrf.rst somewhere in Documentation/ that 
explains the basic principles of how/why for it.  This Documentation 
patch could be the first in the series and then the commit message for 
wireless subsystem can tell people to look at that path for more 
information.

> 
> I'm wondering if WBRF is just a special case of frequency avoidance, and 
> that more generic naming/terminology should be used in core wireless.
> For example, I know there are vendor-specific solutions which allow 
> Wi-Fi to avoid using channels which may conflict with cellular or 
> BlueTooth, and those may benefit from a more generic
>

It seems to me that most vendor solutions that exist don't operate in 
the kernel code but usually in firmware based solutions, right?

I think to come up with a generic solution we need to first have a 
vendor that "wants" to participate in a generic solution to design it 
properly.

>> 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 <evan.quan@amd.com>
>> Signed-off-by: Evan Quan <evan.quan@amd.com>
>> -- 
>> 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)
>> v9->v10:
>>    - get ranges_in->num_of_ranges set and passed in(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        | 105 +++++++++++++++++++++++++++++++++++++
>>   6 files changed, 128 insertions(+)
>>   create mode 100644 net/mac80211/wbrf.c
>>
>> diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
>> index 4b998090898e..f995d06da87f 100644
>> --- a/include/linux/ieee80211.h
>> +++ b/include/linux/ieee80211.h
>> @@ -4335,6 +4335,7 @@ static inline int 
>> ieee80211_get_tdls_action(struct sk_buff *skb, u32 hdr_size)
>>   /* convert frequencies */
>>   #define MHZ_TO_KHZ(freq) ((freq) * 1000)
>>   #define KHZ_TO_MHZ(freq) ((freq) / 1000)
>> +#define KHZ_TO_HZ(freq)  ((freq) * 1000)
>>   #define PR_KHZ(f) KHZ_TO_MHZ(f), f % 1000
>>   #define KHZ_F "%d.%03d"
>> diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
>> index b8de44da1fb8..d46c36f55fd3 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-y += wbrf.o
>> +
>>   ccflags-y += -DDEBUG
>> diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
>> index 68952752b599..458469c224ae 100644
>> --- a/net/mac80211/chan.c
>> +++ b/net/mac80211/chan.c
>> @@ -506,11 +506,16 @@ static void _ieee80211_change_chanctx(struct 
>> ieee80211_local *local,
>>       WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
>> +    ieee80211_remove_wbrf(local, &ctx->conf.def);
>> +
>>       ctx->conf.def = *chandef;
>>       /* check if min chanctx also changed */
>>       changed = IEEE80211_CHANCTX_CHANGE_WIDTH |
>>             _ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for);
>> +
>> +    ieee80211_add_wbrf(local, &ctx->conf.def);
>> +
>>       drv_change_chanctx(local, ctx, changed);
>>       if (!local->use_chanctx) {
>> @@ -668,6 +673,8 @@ static int ieee80211_add_chanctx(struct 
>> ieee80211_local *local,
>>       lockdep_assert_held(&local->mtx);
>>       lockdep_assert_held(&local->chanctx_mtx);
>> +    ieee80211_add_wbrf(local, &ctx->conf.def);
>> +
>>       if (!local->use_chanctx)
>>           local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
>> @@ -748,6 +755,8 @@ static void ieee80211_del_chanctx(struct 
>> ieee80211_local *local,
>>       }
>>       ieee80211_recalc_idle(local);
>> +
>> +    ieee80211_remove_wbrf(local, &ctx->conf.def);
>>   }
>>   static void ieee80211_free_chanctx(struct ieee80211_local *local,
>> diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
>> index 91633a0b723e..719f2c892132 100644
>> --- a/net/mac80211/ieee80211_i.h
>> +++ b/net/mac80211/ieee80211_i.h
>> @@ -1600,6 +1600,8 @@ struct ieee80211_local {
>>       /* extended capabilities provided by mac80211 */
>>       u8 ext_capa[8];
>> +
>> +    bool wbrf_supported;
>>   };
>>   static inline struct ieee80211_sub_if_data *
>> @@ -2638,4 +2640,11 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct 
>> ieee80211_sub_if_data *sdata,
>>                       const struct ieee80211_eht_cap_elem 
>> *eht_cap_ie_elem,
>>                       u8 eht_cap_len,
>>                       struct link_sta_info *link_sta);
>> +
>> +void ieee80211_check_wbrf_support(struct ieee80211_local *local);
>> +void ieee80211_add_wbrf(struct ieee80211_local *local,
>> +            struct cfg80211_chan_def *chandef);
>> +void ieee80211_remove_wbrf(struct ieee80211_local *local,
>> +               struct cfg80211_chan_def *chandef);
>> +
>>   #endif /* IEEE80211_I_H */
>> diff --git a/net/mac80211/main.c b/net/mac80211/main.c
>> index 24315d7b3126..b20bdaac84db 100644
>> --- a/net/mac80211/main.c
>> +++ b/net/mac80211/main.c
>> @@ -1396,6 +1396,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
>>       debugfs_hw_add(local);
>>       rate_control_add_debugfs(local);
>> +    ieee80211_check_wbrf_support(local);
>> +
>>       rtnl_lock();
>>       wiphy_lock(hw->wiphy);
>> diff --git a/net/mac80211/wbrf.c b/net/mac80211/wbrf.c
>> new file mode 100644
>> index 000000000000..63978c7d2bcb
>> --- /dev/null
>> +++ b/net/mac80211/wbrf.c
>> @@ -0,0 +1,105 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Wifi Band Exclusion Interface for WWAN
>> + * Copyright (C) 2023 Advanced Micro Devices
>> + *
>> + */
>> +
>> +#include <linux/acpi_amd_wbrf.h>
>> +#include <net/cfg80211.h>
>> +#include "ieee80211_i.h"
>> +
>> +void ieee80211_check_wbrf_support(struct ieee80211_local *local)
>> +{
>> +    struct wiphy *wiphy = local->hw.wiphy;
>> +    struct device *dev;
>> +
>> +    if (!wiphy)
>> +        return;
>> +
>> +    dev = wiphy->dev.parent;
>> +    if (!dev)
>> +        return;
>> +
>> +    local->wbrf_supported = acpi_amd_wbrf_supported_producer(dev);
> 
> 
> I haven't been looking at this series closely so perhaps this has 
> already been answered, but is this layered correctly? I'm surprised to 
> see core wireless code directly invoking a vendor-specific API.
> Instead should there be a registration mechanism with associated callbacks?
> 
> As I mentioned above I could envision multiple registrants for such an 
> interface.

This feedback was previously shared and earlier versions of the series 
had added another layer to make it generic.

The major complexity is that the semantics of how such a registration 
and notification would work are highly implementation specific.  So the 
generic solution just guessed at how it could work generically and fit 
the ACPI one into it.

The key tenants for a generic solution are:
1) Knowing that you need to notify frequencies
2) Knowing that you need to know about frequencies
3) Knowing which devices in the specific hardware design can be problematic.

It's a lot of complexity that you need deep knowledge of the design to 
do properly.  When this generic concept was brought to drivers/base 
however there was no interest in a generic layer at that time.

So this version of the series returns back to only introducing the AMD 
implementation.  The idea being if another implementation is proposed we 
can look at the overlap between how the two work and could jointly 
introduce a new generic layer.

Hope that explains it well.  As products that need this solution are 
entering the marketplace and can benefit from this kernel work, I hope 
this can be reviewed again soon.

> 
>> +    dev_dbg(dev, "WBRF is %s supported\n",
>> +        local->wbrf_supported ? "" : "not");
>> +}
>> +
>> +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 void get_ranges_from_chandef(struct cfg80211_chan_def *chandef,
>> +                    struct wbrf_ranges_in_out *ranges_in)
>> +{
>> +    u64 start_freq1, end_freq1;
>> +    u64 start_freq2, end_freq2;
>> +    int bandwidth;
>> +
>> +    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;
>> +    ranges_in->num_of_ranges = 1;
>> +
>> +    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;
>> +        ranges_in->num_of_ranges++;
>> +    }
>> +}
>> +
>> +void ieee80211_add_wbrf(struct ieee80211_local *local,
>> +            struct cfg80211_chan_def *chandef)
>> +{
>> +    struct wbrf_ranges_in_out ranges_in = {0};
>> +    struct device *dev;
>> +
>> +    if (!local->wbrf_supported)
>> +        return;
>> +
>> +    dev = local->hw.wiphy->dev.parent;
>> +
>> +    get_ranges_from_chandef(chandef, &ranges_in);
>> +
>> +    acpi_amd_wbrf_add_exclusion(dev, &ranges_in);
>> +}
>> +
>> +void ieee80211_remove_wbrf(struct ieee80211_local *local,
>> +               struct cfg80211_chan_def *chandef)
>> +{
>> +    struct wbrf_ranges_in_out ranges_in = {0};
>> +    struct device *dev;
>> +
>> +    if (!local->wbrf_supported)
>> +        return;
>> +
>> +    dev = local->hw.wiphy->dev.parent;
>> +
>> +    get_ranges_from_chandef(chandef, &ranges_in);
>> +
>> +    acpi_amd_wbrf_remove_exclusion(dev, &ranges_in);
>> +}
> 


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [V11 3/8] wifi: mac80211: Add support for WBRF features
@ 2023-09-06 19:57       ` Mario Limonciello
  0 siblings, 0 replies; 26+ messages in thread
From: Mario Limonciello @ 2023-09-06 19:57 UTC (permalink / raw)
  To: Jeff Johnson, Evan Quan, lenb, johannes, davem, edumazet, kuba,
	pabeni, alexander.deucher, rafael, Lijo.Lazar
  Cc: netdev, linux-wireless, linux-kernel, dri-devel, linux-acpi, amd-gfx

On 9/1/2023 09:32, Jeff Johnson wrote:
> On 8/30/2023 11:20 PM, Evan Quan wrote:
>> To support the WBRF mechanism, Wifi adapters utilized in the system must
> 
> Since this is the first mention of WBRF in the core wireless code IMO 
> you should indicate what this is an acronym for and briefly describe it
> (or add a lore link).

A lot of information is captured in the cover letter and earlier 
commits.  I think you raise a good point that 10 years from now someone 
looking at random commits will have a hard time understanding what 
exactly WBRF stands for.

How about if we introduce a wbrf.rst somewhere in Documentation/ that 
explains the basic principles of how/why for it.  This Documentation 
patch could be the first in the series and then the commit message for 
wireless subsystem can tell people to look at that path for more 
information.

> 
> I'm wondering if WBRF is just a special case of frequency avoidance, and 
> that more generic naming/terminology should be used in core wireless.
> For example, I know there are vendor-specific solutions which allow 
> Wi-Fi to avoid using channels which may conflict with cellular or 
> BlueTooth, and those may benefit from a more generic
>

It seems to me that most vendor solutions that exist don't operate in 
the kernel code but usually in firmware based solutions, right?

I think to come up with a generic solution we need to first have a 
vendor that "wants" to participate in a generic solution to design it 
properly.

>> 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 <evan.quan@amd.com>
>> Signed-off-by: Evan Quan <evan.quan@amd.com>
>> -- 
>> 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)
>> v9->v10:
>>    - get ranges_in->num_of_ranges set and passed in(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        | 105 +++++++++++++++++++++++++++++++++++++
>>   6 files changed, 128 insertions(+)
>>   create mode 100644 net/mac80211/wbrf.c
>>
>> diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
>> index 4b998090898e..f995d06da87f 100644
>> --- a/include/linux/ieee80211.h
>> +++ b/include/linux/ieee80211.h
>> @@ -4335,6 +4335,7 @@ static inline int 
>> ieee80211_get_tdls_action(struct sk_buff *skb, u32 hdr_size)
>>   /* convert frequencies */
>>   #define MHZ_TO_KHZ(freq) ((freq) * 1000)
>>   #define KHZ_TO_MHZ(freq) ((freq) / 1000)
>> +#define KHZ_TO_HZ(freq)  ((freq) * 1000)
>>   #define PR_KHZ(f) KHZ_TO_MHZ(f), f % 1000
>>   #define KHZ_F "%d.%03d"
>> diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
>> index b8de44da1fb8..d46c36f55fd3 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-y += wbrf.o
>> +
>>   ccflags-y += -DDEBUG
>> diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
>> index 68952752b599..458469c224ae 100644
>> --- a/net/mac80211/chan.c
>> +++ b/net/mac80211/chan.c
>> @@ -506,11 +506,16 @@ static void _ieee80211_change_chanctx(struct 
>> ieee80211_local *local,
>>       WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
>> +    ieee80211_remove_wbrf(local, &ctx->conf.def);
>> +
>>       ctx->conf.def = *chandef;
>>       /* check if min chanctx also changed */
>>       changed = IEEE80211_CHANCTX_CHANGE_WIDTH |
>>             _ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for);
>> +
>> +    ieee80211_add_wbrf(local, &ctx->conf.def);
>> +
>>       drv_change_chanctx(local, ctx, changed);
>>       if (!local->use_chanctx) {
>> @@ -668,6 +673,8 @@ static int ieee80211_add_chanctx(struct 
>> ieee80211_local *local,
>>       lockdep_assert_held(&local->mtx);
>>       lockdep_assert_held(&local->chanctx_mtx);
>> +    ieee80211_add_wbrf(local, &ctx->conf.def);
>> +
>>       if (!local->use_chanctx)
>>           local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
>> @@ -748,6 +755,8 @@ static void ieee80211_del_chanctx(struct 
>> ieee80211_local *local,
>>       }
>>       ieee80211_recalc_idle(local);
>> +
>> +    ieee80211_remove_wbrf(local, &ctx->conf.def);
>>   }
>>   static void ieee80211_free_chanctx(struct ieee80211_local *local,
>> diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
>> index 91633a0b723e..719f2c892132 100644
>> --- a/net/mac80211/ieee80211_i.h
>> +++ b/net/mac80211/ieee80211_i.h
>> @@ -1600,6 +1600,8 @@ struct ieee80211_local {
>>       /* extended capabilities provided by mac80211 */
>>       u8 ext_capa[8];
>> +
>> +    bool wbrf_supported;
>>   };
>>   static inline struct ieee80211_sub_if_data *
>> @@ -2638,4 +2640,11 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct 
>> ieee80211_sub_if_data *sdata,
>>                       const struct ieee80211_eht_cap_elem 
>> *eht_cap_ie_elem,
>>                       u8 eht_cap_len,
>>                       struct link_sta_info *link_sta);
>> +
>> +void ieee80211_check_wbrf_support(struct ieee80211_local *local);
>> +void ieee80211_add_wbrf(struct ieee80211_local *local,
>> +            struct cfg80211_chan_def *chandef);
>> +void ieee80211_remove_wbrf(struct ieee80211_local *local,
>> +               struct cfg80211_chan_def *chandef);
>> +
>>   #endif /* IEEE80211_I_H */
>> diff --git a/net/mac80211/main.c b/net/mac80211/main.c
>> index 24315d7b3126..b20bdaac84db 100644
>> --- a/net/mac80211/main.c
>> +++ b/net/mac80211/main.c
>> @@ -1396,6 +1396,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
>>       debugfs_hw_add(local);
>>       rate_control_add_debugfs(local);
>> +    ieee80211_check_wbrf_support(local);
>> +
>>       rtnl_lock();
>>       wiphy_lock(hw->wiphy);
>> diff --git a/net/mac80211/wbrf.c b/net/mac80211/wbrf.c
>> new file mode 100644
>> index 000000000000..63978c7d2bcb
>> --- /dev/null
>> +++ b/net/mac80211/wbrf.c
>> @@ -0,0 +1,105 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Wifi Band Exclusion Interface for WWAN
>> + * Copyright (C) 2023 Advanced Micro Devices
>> + *
>> + */
>> +
>> +#include <linux/acpi_amd_wbrf.h>
>> +#include <net/cfg80211.h>
>> +#include "ieee80211_i.h"
>> +
>> +void ieee80211_check_wbrf_support(struct ieee80211_local *local)
>> +{
>> +    struct wiphy *wiphy = local->hw.wiphy;
>> +    struct device *dev;
>> +
>> +    if (!wiphy)
>> +        return;
>> +
>> +    dev = wiphy->dev.parent;
>> +    if (!dev)
>> +        return;
>> +
>> +    local->wbrf_supported = acpi_amd_wbrf_supported_producer(dev);
> 
> 
> I haven't been looking at this series closely so perhaps this has 
> already been answered, but is this layered correctly? I'm surprised to 
> see core wireless code directly invoking a vendor-specific API.
> Instead should there be a registration mechanism with associated callbacks?
> 
> As I mentioned above I could envision multiple registrants for such an 
> interface.

This feedback was previously shared and earlier versions of the series 
had added another layer to make it generic.

The major complexity is that the semantics of how such a registration 
and notification would work are highly implementation specific.  So the 
generic solution just guessed at how it could work generically and fit 
the ACPI one into it.

The key tenants for a generic solution are:
1) Knowing that you need to notify frequencies
2) Knowing that you need to know about frequencies
3) Knowing which devices in the specific hardware design can be problematic.

It's a lot of complexity that you need deep knowledge of the design to 
do properly.  When this generic concept was brought to drivers/base 
however there was no interest in a generic layer at that time.

So this version of the series returns back to only introducing the AMD 
implementation.  The idea being if another implementation is proposed we 
can look at the overlap between how the two work and could jointly 
introduce a new generic layer.

Hope that explains it well.  As products that need this solution are 
entering the marketplace and can benefit from this kernel work, I hope 
this can be reviewed again soon.

> 
>> +    dev_dbg(dev, "WBRF is %s supported\n",
>> +        local->wbrf_supported ? "" : "not");
>> +}
>> +
>> +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 void get_ranges_from_chandef(struct cfg80211_chan_def *chandef,
>> +                    struct wbrf_ranges_in_out *ranges_in)
>> +{
>> +    u64 start_freq1, end_freq1;
>> +    u64 start_freq2, end_freq2;
>> +    int bandwidth;
>> +
>> +    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;
>> +    ranges_in->num_of_ranges = 1;
>> +
>> +    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;
>> +        ranges_in->num_of_ranges++;
>> +    }
>> +}
>> +
>> +void ieee80211_add_wbrf(struct ieee80211_local *local,
>> +            struct cfg80211_chan_def *chandef)
>> +{
>> +    struct wbrf_ranges_in_out ranges_in = {0};
>> +    struct device *dev;
>> +
>> +    if (!local->wbrf_supported)
>> +        return;
>> +
>> +    dev = local->hw.wiphy->dev.parent;
>> +
>> +    get_ranges_from_chandef(chandef, &ranges_in);
>> +
>> +    acpi_amd_wbrf_add_exclusion(dev, &ranges_in);
>> +}
>> +
>> +void ieee80211_remove_wbrf(struct ieee80211_local *local,
>> +               struct cfg80211_chan_def *chandef)
>> +{
>> +    struct wbrf_ranges_in_out ranges_in = {0};
>> +    struct device *dev;
>> +
>> +    if (!local->wbrf_supported)
>> +        return;
>> +
>> +    dev = local->hw.wiphy->dev.parent;
>> +
>> +    get_ranges_from_chandef(chandef, &ranges_in);
>> +
>> +    acpi_amd_wbrf_remove_exclusion(dev, &ranges_in);
>> +}
> 


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [V11 1/8] ACPI: Add support for AMD ACPI based Wifi band RFI mitigation feature
  2023-08-31  6:20   ` Evan Quan
@ 2023-09-18 13:04     ` Mario Limonciello
  -1 siblings, 0 replies; 26+ messages in thread
From: Mario Limonciello @ 2023-09-18 13:04 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: pabeni, netdev, Lijo.Lazar, linux-wireless, linux-kernel,
	dri-devel, linux-acpi, edumazet, amd-gfx, alexander.deucher,
	kuba, johannes, Evan Quan, davem, lenb

On 8/31/2023 01:20, Evan Quan 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.
> 
> Signed-off-by: Evan Quan <evan.quan@amd.com>
> --
> v10->v11:
>    - fix typo(Simon)

Rafael,

Friendly ping on reviewing patch 1 from v11.

Thank you,

> ---
>   drivers/acpi/Kconfig          |  17 ++
>   drivers/acpi/Makefile         |   2 +
>   drivers/acpi/amd_wbrf.c       | 414 ++++++++++++++++++++++++++++++++++
>   include/linux/acpi_amd_wbrf.h | 140 ++++++++++++
>   4 files changed, 573 insertions(+)
>   create mode 100644 drivers/acpi/amd_wbrf.c
>   create mode 100644 include/linux/acpi_amd_wbrf.h
> 
> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> index 00dd309b6682..a092ea72d152 100644
> --- a/drivers/acpi/Kconfig
> +++ b/drivers/acpi/Kconfig
> @@ -594,6 +594,23 @@ config ACPI_PRMT
>   	  substantially increase computational overhead related to the
>   	  initialization of some server systems.
>   
> +config WBRF_AMD_ACPI
> +	bool "ACPI based WBRF mechanism introduced by AMD"
> +	depends on ACPI
> +	default n
> +	help
> +	  Wifi band RFI mitigation mechanism allows multiple drivers from
> +	  different domains to notify the frequencies in use so that hardware
> +	  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.
> +
>   endif	# ACPI
>   
>   config X86_PM_TIMER
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index eaa09bf52f17..a3d2f259d0a5 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -132,3 +132,5 @@ obj-$(CONFIG_ARM64)		+= arm64/
>   obj-$(CONFIG_ACPI_VIOT)		+= viot.o
>   
>   obj-$(CONFIG_RISCV)		+= riscv/
> +
> +obj-$(CONFIG_WBRF_AMD_ACPI)	+= amd_wbrf.o
> diff --git a/drivers/acpi/amd_wbrf.c b/drivers/acpi/amd_wbrf.c
> new file mode 100644
> index 000000000000..8ee0e2977a30
> --- /dev/null
> +++ b/drivers/acpi/amd_wbrf.c
> @@ -0,0 +1,414 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Wifi Band Exclusion Interface (AMD ACPI Implementation)
> + * Copyright (C) 2023 Advanced Micro Devices
> + *
> + */
> +
> +#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 naturally aligned.
> + * And unfortunately the design has been settled down.
> + */
> +struct amd_wbrf_ranges_out {
> +	u32			num_of_ranges;
> +	struct exclusion_range	band_list[MAX_NUM_OF_WBRF_RANGES];
> +} __packed;
> +
> +static const guid_t wifi_acpi_dsm_guid =
> +	GUID_INIT(0x7b7656cf, 0xdc3d, 0x4c1c,
> +		  0x83, 0xe9, 0x66, 0xe7, 0x21, 0xde, 0x30, 0x70);
> +
> +static BLOCKING_NOTIFIER_HEAD(wbrf_chain_head);
> +
> +static int wbrf_dsm(struct acpi_device *adev,
> +		    u8 fn,
> +		    union acpi_object *argv4)
> +{
> +	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_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_out *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++;
> +
> +	/*
> +	 * The valid entry counter does not match with this told.
> +	 * Something must went wrong.
> +	 */
> +	if (num_of_ranges != in->num_of_ranges)
> +		return -EINVAL;
> +
> +	/*
> +	 * Every input frequency band comes with two end points(start/end)
> +	 * and each is accounted as an element. Meanwhile the range count
> +	 * and action type are accounted as an element each.
> +	 * So, the total element count = 2 * num_of_ranges + 1 + 1.
> +	 */
> +	num_of_elements = 2 * num_of_ranges + 1 + 1;
> +
> +	tmp = kcalloc(num_of_elements, sizeof(*tmp), GFP_KERNEL);
> +	if (!tmp)
> +		return -ENOMEM;
> +
> +	argv4.package.type = ACPI_TYPE_PACKAGE;
> +	argv4.package.count = num_of_elements;
> +	argv4.package.elements = tmp;
> +
> +	tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
> +	tmp[arg_idx++].integer.value = num_of_ranges;
> +	tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
> +	tmp[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;
> +
> +		tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
> +		tmp[arg_idx++].integer.value = in->band_list[loop_idx].start;
> +		tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
> +		tmp[arg_idx++].integer.value = in->band_list[loop_idx].end;
> +	}
> +
> +	ret = wbrf_dsm(adev, WBRF_RECORD, &argv4);
> +
> +	kfree(tmp);
> +
> +	return ret;
> +}
> +
> +/**
> + * acpi_amd_wbrf_add_exclusion - broadcast the frequency band the device
> + *                               is using
> + *
> + * @dev: device pointer
> + * @in: input structure containing the frequency band the device is using
> + *
> + * Broadcast to other consumers the frequency band the device starts
> + * to use. Underneath the surface the information is cached into an
> + * internal buffer first. Then a notification is sent to all those
> + * registered consumers. So then they can retrieve that buffer to
> + * know the latest active frequency bands. The benefit with such design
> + * is for those consumers which have not been registered yet, they can
> + * still have a chance to retrieve such information later.
> + */
> +int acpi_amd_wbrf_add_exclusion(struct device *dev,
> +				struct wbrf_ranges_in_out *in)
> +{
> +	struct acpi_device *adev = ACPI_COMPANION(dev);
> +	int ret;
> +
> +	if (!adev)
> +		return -ENODEV;
> +
> +	ret = wbrf_record(adev, WBRF_RECORD_ADD, in);
> +	if (ret)
> +		return ret;
> +
> +	blocking_notifier_call_chain(&wbrf_chain_head,
> +				     WBRF_CHANGED,
> +				     NULL);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_add_exclusion);
> +
> +/**
> + * acpi_amd_wbrf_remove_exclusion - broadcast the frequency band the device
> + *                                  is no longer using
> + *
> + * @dev: device pointer
> + * @in: input structure containing the frequency band which is not used
> + *      by the device any more
> + *
> + * Broadcast to other consumers the frequency band the device stops
> + * to use. The stored information paired with this will be dropped
> + * from the internal buffer. And then a notification is sent to
> + * all registered consumers.
> + */
> +int acpi_amd_wbrf_remove_exclusion(struct device *dev,
> +				   struct wbrf_ranges_in_out *in)
> +{
> +	struct acpi_device *adev = ACPI_COMPANION(dev);
> +	int ret;
> +
> +	if (!adev)
> +		return -ENODEV;
> +
> +	ret = wbrf_record(adev, WBRF_RECORD_REMOVE, in);
> +	if (ret)
> +		return ret;
> +
> +	blocking_notifier_call_chain(&wbrf_chain_head,
> +				     WBRF_CHANGED,
> +				     NULL);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_remove_exclusion);
> +
> +static bool acpi_amd_wbrf_supported_system(void)
> +{
> +	acpi_status status;
> +	acpi_handle handle;
> +
> +	status = acpi_get_handle(NULL, ACPI_AMD_WBRF_METHOD, &handle);
> +
> +	return ACPI_SUCCESS(status);
> +}
> +
> +/**
> + * acpi_amd_wbrf_supported_producer - determine if the WBRF can be enabled
> + *                                    for the device as a producer
> + *
> + * @dev: device pointer
> + *
> + * Determine if the platform equipped with necessary implementations to
> + * support WBRF for the device as a producer.
> + */
> +bool acpi_amd_wbrf_supported_producer(struct device *dev)
> +{
> +	struct acpi_device *adev = ACPI_COMPANION(dev);
> +
> +	if (!acpi_amd_wbrf_supported_system())
> +		return false;
> +
> +	if (!adev)
> +		return false;
> +
> +	return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid,
> +			      WBRF_REVISION,
> +			      BIT(WBRF_RECORD));
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_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_FAILURE(ret))
> +		return NULL;
> +
> +	return buf.pointer;
> +}
> +
> +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;
> +
> +	ACPI_FREE(obj);
> +
> +	return mask & BIT(WBRF_ENABLED) && (mask & funcs) == funcs;
> +}
> +
> +/**
> + * acpi_amd_wbrf_supported_consumer - determine if the WBRF can be enabled
> + *                                    for the device as a consumer
> + *
> + * @dev: device pointer
> + *
> + * Determine if the platform equipped with necessary implementations to
> + * support WBRF for the device as a consumer.
> + */
> +bool acpi_amd_wbrf_supported_consumer(struct device *dev)
> +{
> +	struct acpi_device *adev = ACPI_COMPANION(dev);
> +
> +	if (!acpi_amd_wbrf_supported_system())
> +		return false;
> +
> +	if (!adev)
> +		return false;
> +
> +	return check_acpi_wbrf(adev->handle,
> +			       WBRF_REVISION,
> +			       BIT(WBRF_RETRIEVE));
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_supported_consumer);
> +
> +/**
> + * acpi_amd_wbrf_retrieve_exclusions - retrieve current active frequency
> + *                                     bands
> + *
> + * @dev: device pointer
> + * @out: output structure containing all the active frequency bands
> + *
> + * Retrieve the current active frequency bands which were broadcasted
> + * by other producers. The consumer who calls this API should take
> + * proper actions if any of the frequency band may cause RFI with its
> + * own frequency band used.
> + */
> +int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
> +				      struct wbrf_ranges_in_out *out)
> +{
> +	struct acpi_device *adev = ACPI_COMPANION(dev);
> +	struct amd_wbrf_ranges_out acpi_out = {0};
> +	union acpi_object *obj;
> +	int ret = 0;
> +
> +	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, "Wrong sized WBRT information");
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +	memcpy(&acpi_out, obj->buffer.pointer, obj->buffer.length);
> +
> +	out->num_of_ranges = acpi_out.num_of_ranges;
> +	memcpy(out->band_list, acpi_out.band_list, sizeof(acpi_out.band_list));
> +
> +out:
> +	ACPI_FREE(obj);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_retrieve_exclusions);
> +
> +/**
> + * acpi_amd_wbrf_register_notifier - register for notifications of frequency
> + *                                   band update
> + *
> + * @nb: driver notifier block
> + *
> + * The consumer should register itself via this API. So that it can get
> + * notified timely on the frequency band updates from other producers.
> + */
> +int acpi_amd_wbrf_register_notifier(struct notifier_block *nb)
> +{
> +	return blocking_notifier_chain_register(&wbrf_chain_head, nb);
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_register_notifier);
> +
> +/**
> + * acpi_amd_wbrf_unregister_notifier - unregister for notifications of
> + *                                     frequency band update
> + *
> + * @nb: driver notifier block
> + *
> + * The consumer should call this API when it is longer interested with
> + * the frequency band updates from other producers. Usually, this should
> + * be performed during driver cleanup.
> + */
> +int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb)
> +{
> +	return blocking_notifier_chain_unregister(&wbrf_chain_head, nb);
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_unregister_notifier);
> diff --git a/include/linux/acpi_amd_wbrf.h b/include/linux/acpi_amd_wbrf.h
> new file mode 100644
> index 000000000000..c2363d664641
> --- /dev/null
> +++ b/include/linux/acpi_amd_wbrf.h
> @@ -0,0 +1,140 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Wifi Band Exclusion Interface (AMD ACPI Implementation)
> + * Copyright (C) 2023 Advanced Micro Devices
> + *
> + * 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 to support
> + * WBRF(Wifi Band RFI mitigation Feature) for platforms with AMD dGPU + WLAN.
> + * This needs support from BIOS equipped with necessary AML implementations
> + * and dGPU firmwares.
> + *
> + * Some general terms:
> + * Producer: such component who can produce high-powered radio frequency
> + * Consumer: such component who can adjust its in-use frequency in
> + *           response to the radio frequencies of other components to
> + *           mitigate the possible RFI.
> + *
> + * To make the mechanism function, those producers should notify active use
> + * of their particular frequencies so that other consumers can make relative
> + * internal adjustments as necessary to avoid this resonance.
> + */
> +
> +#ifndef _ACPI_AMD_WBRF_H
> +#define _ACPI_AMD_WBRF_H
> +
> +#include <linux/device.h>
> +#include <linux/notifier.h>
> +
> +/*
> + * A wbrf range is defined as a frequency band with start and end
> + * frequency point specified(in Hz). And a vaild range should have
> + * its start and end frequency point filled with non-zero values.
> + * Meanwhile, the maximum number of wbrf ranges is limited as
> + * `MAX_NUM_OF_WBRF_RANGES`.
> + */
> +#define MAX_NUM_OF_WBRF_RANGES		11
> +
> +struct exclusion_range {
> +	u64		start;
> +	u64		end;
> +};
> +
> +struct wbrf_ranges_in_out {
> +	u64			num_of_ranges;
> +	struct exclusion_range	band_list[MAX_NUM_OF_WBRF_RANGES];
> +};
> +
> +/*
> + * The notification types for the consumers are defined as below.
> + * The consumers may need to take different actions in response to
> + * different notifications.
> + * WBRF_CHANGED: there was some frequency band updates. The consumers
> + *               should retrieve the latest active frequency bands.
> + */
> +enum wbrf_notifier_actions {
> +	WBRF_CHANGED,
> +};
> +
> +#if IS_ENABLED(CONFIG_WBRF_AMD_ACPI)
> +/*
> + * The expected flow for the producers:
> + * 1) During probe, call `acpi_amd_wbrf_supported_producer` to check
> + *    if WBRF can be enabled for the device.
> + * 2) On using some frequency band, call `acpi_amd_wbrf_add_exclusion`
> + *    to get other consumers properly notified.
> + * 3) Or on stopping using some frequency band, call
> + *    `acpi_amd_wbrf_remove_exclusion` to get other consumers notified.
> + */
> +bool acpi_amd_wbrf_supported_producer(struct device *dev);
> +int acpi_amd_wbrf_remove_exclusion(struct device *dev,
> +				   struct wbrf_ranges_in_out *in);
> +int acpi_amd_wbrf_add_exclusion(struct device *dev,
> +				struct wbrf_ranges_in_out *in);
> +
> +/*
> + * The expected flow for the consumers:
> + * 1) During probe, call `acpi_amd_wbrf_supported_consumer` to check if WBRF
> + *    can be enabled for the device.
> + * 2) Call `acpi_amd_wbrf_register_notifier` to register for notification
> + *    of frequency band change(add or remove) from other producers.
> + * 3) Call the `acpi_amd_wbrf_retrieve_exclusions` intentionally to retrieve
> + *    current active frequency bands considering some producers may broadcast
> + *    such information before the consumer is up.
> + * 4) On receiving a notification for frequency band change, run
> + *    `acpi_amd_wbrf_retrieve_exclusions` again to retrieve the latest
> + *    active frequency bands.
> + * 5) During driver cleanup, call `acpi_amd_wbrf_unregister_notifier` to
> + *    unregister the notifier.
> + */
> +bool acpi_amd_wbrf_supported_consumer(struct device *dev);
> +int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
> +				      struct wbrf_ranges_in_out *out);
> +int acpi_amd_wbrf_register_notifier(struct notifier_block *nb);
> +int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb);
> +#else
> +static inline
> +bool acpi_amd_wbrf_supported_consumer(struct device *dev)
> +{
> +	return false;
> +}
> +static inline
> +int acpi_amd_wbrf_remove_exclusion(struct device *dev,
> +				   struct wbrf_ranges_in_out *in)
> +{
> +	return -ENODEV;
> +}
> +static inline
> +int acpi_amd_wbrf_add_exclusion(struct device *dev,
> +				struct wbrf_ranges_in_out *in)
> +{
> +	return -ENODEV;
> +}
> +static inline
> +bool acpi_amd_wbrf_supported_producer(struct device *dev)
> +{
> +	return false;
> +}
> +static inline
> +int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
> +				      struct wbrf_ranges_in_out *out)
> +{
> +	return -ENODEV;
> +}
> +static inline
> +int acpi_amd_wbrf_register_notifier(struct notifier_block *nb)
> +{
> +	return -ENODEV;
> +}
> +static inline
> +int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb)
> +{
> +	return -ENODEV;
> +}
> +#endif
> +
> +#endif /* _ACPI_AMD_WBRF_H */


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [V11 1/8] ACPI: Add support for AMD ACPI based Wifi band RFI mitigation feature
@ 2023-09-18 13:04     ` Mario Limonciello
  0 siblings, 0 replies; 26+ messages in thread
From: Mario Limonciello @ 2023-09-18 13:04 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-kernel, linux-acpi, amd-gfx, dri-devel, linux-wireless,
	netdev, Evan Quan, lenb, johannes, davem, edumazet, kuba, pabeni,
	alexander.deucher, Lijo.Lazar

On 8/31/2023 01:20, Evan Quan 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.
> 
> Signed-off-by: Evan Quan <evan.quan@amd.com>
> --
> v10->v11:
>    - fix typo(Simon)

Rafael,

Friendly ping on reviewing patch 1 from v11.

Thank you,

> ---
>   drivers/acpi/Kconfig          |  17 ++
>   drivers/acpi/Makefile         |   2 +
>   drivers/acpi/amd_wbrf.c       | 414 ++++++++++++++++++++++++++++++++++
>   include/linux/acpi_amd_wbrf.h | 140 ++++++++++++
>   4 files changed, 573 insertions(+)
>   create mode 100644 drivers/acpi/amd_wbrf.c
>   create mode 100644 include/linux/acpi_amd_wbrf.h
> 
> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> index 00dd309b6682..a092ea72d152 100644
> --- a/drivers/acpi/Kconfig
> +++ b/drivers/acpi/Kconfig
> @@ -594,6 +594,23 @@ config ACPI_PRMT
>   	  substantially increase computational overhead related to the
>   	  initialization of some server systems.
>   
> +config WBRF_AMD_ACPI
> +	bool "ACPI based WBRF mechanism introduced by AMD"
> +	depends on ACPI
> +	default n
> +	help
> +	  Wifi band RFI mitigation mechanism allows multiple drivers from
> +	  different domains to notify the frequencies in use so that hardware
> +	  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.
> +
>   endif	# ACPI
>   
>   config X86_PM_TIMER
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index eaa09bf52f17..a3d2f259d0a5 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -132,3 +132,5 @@ obj-$(CONFIG_ARM64)		+= arm64/
>   obj-$(CONFIG_ACPI_VIOT)		+= viot.o
>   
>   obj-$(CONFIG_RISCV)		+= riscv/
> +
> +obj-$(CONFIG_WBRF_AMD_ACPI)	+= amd_wbrf.o
> diff --git a/drivers/acpi/amd_wbrf.c b/drivers/acpi/amd_wbrf.c
> new file mode 100644
> index 000000000000..8ee0e2977a30
> --- /dev/null
> +++ b/drivers/acpi/amd_wbrf.c
> @@ -0,0 +1,414 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Wifi Band Exclusion Interface (AMD ACPI Implementation)
> + * Copyright (C) 2023 Advanced Micro Devices
> + *
> + */
> +
> +#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 naturally aligned.
> + * And unfortunately the design has been settled down.
> + */
> +struct amd_wbrf_ranges_out {
> +	u32			num_of_ranges;
> +	struct exclusion_range	band_list[MAX_NUM_OF_WBRF_RANGES];
> +} __packed;
> +
> +static const guid_t wifi_acpi_dsm_guid =
> +	GUID_INIT(0x7b7656cf, 0xdc3d, 0x4c1c,
> +		  0x83, 0xe9, 0x66, 0xe7, 0x21, 0xde, 0x30, 0x70);
> +
> +static BLOCKING_NOTIFIER_HEAD(wbrf_chain_head);
> +
> +static int wbrf_dsm(struct acpi_device *adev,
> +		    u8 fn,
> +		    union acpi_object *argv4)
> +{
> +	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_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_out *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++;
> +
> +	/*
> +	 * The valid entry counter does not match with this told.
> +	 * Something must went wrong.
> +	 */
> +	if (num_of_ranges != in->num_of_ranges)
> +		return -EINVAL;
> +
> +	/*
> +	 * Every input frequency band comes with two end points(start/end)
> +	 * and each is accounted as an element. Meanwhile the range count
> +	 * and action type are accounted as an element each.
> +	 * So, the total element count = 2 * num_of_ranges + 1 + 1.
> +	 */
> +	num_of_elements = 2 * num_of_ranges + 1 + 1;
> +
> +	tmp = kcalloc(num_of_elements, sizeof(*tmp), GFP_KERNEL);
> +	if (!tmp)
> +		return -ENOMEM;
> +
> +	argv4.package.type = ACPI_TYPE_PACKAGE;
> +	argv4.package.count = num_of_elements;
> +	argv4.package.elements = tmp;
> +
> +	tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
> +	tmp[arg_idx++].integer.value = num_of_ranges;
> +	tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
> +	tmp[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;
> +
> +		tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
> +		tmp[arg_idx++].integer.value = in->band_list[loop_idx].start;
> +		tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
> +		tmp[arg_idx++].integer.value = in->band_list[loop_idx].end;
> +	}
> +
> +	ret = wbrf_dsm(adev, WBRF_RECORD, &argv4);
> +
> +	kfree(tmp);
> +
> +	return ret;
> +}
> +
> +/**
> + * acpi_amd_wbrf_add_exclusion - broadcast the frequency band the device
> + *                               is using
> + *
> + * @dev: device pointer
> + * @in: input structure containing the frequency band the device is using
> + *
> + * Broadcast to other consumers the frequency band the device starts
> + * to use. Underneath the surface the information is cached into an
> + * internal buffer first. Then a notification is sent to all those
> + * registered consumers. So then they can retrieve that buffer to
> + * know the latest active frequency bands. The benefit with such design
> + * is for those consumers which have not been registered yet, they can
> + * still have a chance to retrieve such information later.
> + */
> +int acpi_amd_wbrf_add_exclusion(struct device *dev,
> +				struct wbrf_ranges_in_out *in)
> +{
> +	struct acpi_device *adev = ACPI_COMPANION(dev);
> +	int ret;
> +
> +	if (!adev)
> +		return -ENODEV;
> +
> +	ret = wbrf_record(adev, WBRF_RECORD_ADD, in);
> +	if (ret)
> +		return ret;
> +
> +	blocking_notifier_call_chain(&wbrf_chain_head,
> +				     WBRF_CHANGED,
> +				     NULL);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_add_exclusion);
> +
> +/**
> + * acpi_amd_wbrf_remove_exclusion - broadcast the frequency band the device
> + *                                  is no longer using
> + *
> + * @dev: device pointer
> + * @in: input structure containing the frequency band which is not used
> + *      by the device any more
> + *
> + * Broadcast to other consumers the frequency band the device stops
> + * to use. The stored information paired with this will be dropped
> + * from the internal buffer. And then a notification is sent to
> + * all registered consumers.
> + */
> +int acpi_amd_wbrf_remove_exclusion(struct device *dev,
> +				   struct wbrf_ranges_in_out *in)
> +{
> +	struct acpi_device *adev = ACPI_COMPANION(dev);
> +	int ret;
> +
> +	if (!adev)
> +		return -ENODEV;
> +
> +	ret = wbrf_record(adev, WBRF_RECORD_REMOVE, in);
> +	if (ret)
> +		return ret;
> +
> +	blocking_notifier_call_chain(&wbrf_chain_head,
> +				     WBRF_CHANGED,
> +				     NULL);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_remove_exclusion);
> +
> +static bool acpi_amd_wbrf_supported_system(void)
> +{
> +	acpi_status status;
> +	acpi_handle handle;
> +
> +	status = acpi_get_handle(NULL, ACPI_AMD_WBRF_METHOD, &handle);
> +
> +	return ACPI_SUCCESS(status);
> +}
> +
> +/**
> + * acpi_amd_wbrf_supported_producer - determine if the WBRF can be enabled
> + *                                    for the device as a producer
> + *
> + * @dev: device pointer
> + *
> + * Determine if the platform equipped with necessary implementations to
> + * support WBRF for the device as a producer.
> + */
> +bool acpi_amd_wbrf_supported_producer(struct device *dev)
> +{
> +	struct acpi_device *adev = ACPI_COMPANION(dev);
> +
> +	if (!acpi_amd_wbrf_supported_system())
> +		return false;
> +
> +	if (!adev)
> +		return false;
> +
> +	return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid,
> +			      WBRF_REVISION,
> +			      BIT(WBRF_RECORD));
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_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_FAILURE(ret))
> +		return NULL;
> +
> +	return buf.pointer;
> +}
> +
> +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;
> +
> +	ACPI_FREE(obj);
> +
> +	return mask & BIT(WBRF_ENABLED) && (mask & funcs) == funcs;
> +}
> +
> +/**
> + * acpi_amd_wbrf_supported_consumer - determine if the WBRF can be enabled
> + *                                    for the device as a consumer
> + *
> + * @dev: device pointer
> + *
> + * Determine if the platform equipped with necessary implementations to
> + * support WBRF for the device as a consumer.
> + */
> +bool acpi_amd_wbrf_supported_consumer(struct device *dev)
> +{
> +	struct acpi_device *adev = ACPI_COMPANION(dev);
> +
> +	if (!acpi_amd_wbrf_supported_system())
> +		return false;
> +
> +	if (!adev)
> +		return false;
> +
> +	return check_acpi_wbrf(adev->handle,
> +			       WBRF_REVISION,
> +			       BIT(WBRF_RETRIEVE));
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_supported_consumer);
> +
> +/**
> + * acpi_amd_wbrf_retrieve_exclusions - retrieve current active frequency
> + *                                     bands
> + *
> + * @dev: device pointer
> + * @out: output structure containing all the active frequency bands
> + *
> + * Retrieve the current active frequency bands which were broadcasted
> + * by other producers. The consumer who calls this API should take
> + * proper actions if any of the frequency band may cause RFI with its
> + * own frequency band used.
> + */
> +int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
> +				      struct wbrf_ranges_in_out *out)
> +{
> +	struct acpi_device *adev = ACPI_COMPANION(dev);
> +	struct amd_wbrf_ranges_out acpi_out = {0};
> +	union acpi_object *obj;
> +	int ret = 0;
> +
> +	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, "Wrong sized WBRT information");
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +	memcpy(&acpi_out, obj->buffer.pointer, obj->buffer.length);
> +
> +	out->num_of_ranges = acpi_out.num_of_ranges;
> +	memcpy(out->band_list, acpi_out.band_list, sizeof(acpi_out.band_list));
> +
> +out:
> +	ACPI_FREE(obj);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_retrieve_exclusions);
> +
> +/**
> + * acpi_amd_wbrf_register_notifier - register for notifications of frequency
> + *                                   band update
> + *
> + * @nb: driver notifier block
> + *
> + * The consumer should register itself via this API. So that it can get
> + * notified timely on the frequency band updates from other producers.
> + */
> +int acpi_amd_wbrf_register_notifier(struct notifier_block *nb)
> +{
> +	return blocking_notifier_chain_register(&wbrf_chain_head, nb);
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_register_notifier);
> +
> +/**
> + * acpi_amd_wbrf_unregister_notifier - unregister for notifications of
> + *                                     frequency band update
> + *
> + * @nb: driver notifier block
> + *
> + * The consumer should call this API when it is longer interested with
> + * the frequency band updates from other producers. Usually, this should
> + * be performed during driver cleanup.
> + */
> +int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb)
> +{
> +	return blocking_notifier_chain_unregister(&wbrf_chain_head, nb);
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_unregister_notifier);
> diff --git a/include/linux/acpi_amd_wbrf.h b/include/linux/acpi_amd_wbrf.h
> new file mode 100644
> index 000000000000..c2363d664641
> --- /dev/null
> +++ b/include/linux/acpi_amd_wbrf.h
> @@ -0,0 +1,140 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Wifi Band Exclusion Interface (AMD ACPI Implementation)
> + * Copyright (C) 2023 Advanced Micro Devices
> + *
> + * 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 to support
> + * WBRF(Wifi Band RFI mitigation Feature) for platforms with AMD dGPU + WLAN.
> + * This needs support from BIOS equipped with necessary AML implementations
> + * and dGPU firmwares.
> + *
> + * Some general terms:
> + * Producer: such component who can produce high-powered radio frequency
> + * Consumer: such component who can adjust its in-use frequency in
> + *           response to the radio frequencies of other components to
> + *           mitigate the possible RFI.
> + *
> + * To make the mechanism function, those producers should notify active use
> + * of their particular frequencies so that other consumers can make relative
> + * internal adjustments as necessary to avoid this resonance.
> + */
> +
> +#ifndef _ACPI_AMD_WBRF_H
> +#define _ACPI_AMD_WBRF_H
> +
> +#include <linux/device.h>
> +#include <linux/notifier.h>
> +
> +/*
> + * A wbrf range is defined as a frequency band with start and end
> + * frequency point specified(in Hz). And a vaild range should have
> + * its start and end frequency point filled with non-zero values.
> + * Meanwhile, the maximum number of wbrf ranges is limited as
> + * `MAX_NUM_OF_WBRF_RANGES`.
> + */
> +#define MAX_NUM_OF_WBRF_RANGES		11
> +
> +struct exclusion_range {
> +	u64		start;
> +	u64		end;
> +};
> +
> +struct wbrf_ranges_in_out {
> +	u64			num_of_ranges;
> +	struct exclusion_range	band_list[MAX_NUM_OF_WBRF_RANGES];
> +};
> +
> +/*
> + * The notification types for the consumers are defined as below.
> + * The consumers may need to take different actions in response to
> + * different notifications.
> + * WBRF_CHANGED: there was some frequency band updates. The consumers
> + *               should retrieve the latest active frequency bands.
> + */
> +enum wbrf_notifier_actions {
> +	WBRF_CHANGED,
> +};
> +
> +#if IS_ENABLED(CONFIG_WBRF_AMD_ACPI)
> +/*
> + * The expected flow for the producers:
> + * 1) During probe, call `acpi_amd_wbrf_supported_producer` to check
> + *    if WBRF can be enabled for the device.
> + * 2) On using some frequency band, call `acpi_amd_wbrf_add_exclusion`
> + *    to get other consumers properly notified.
> + * 3) Or on stopping using some frequency band, call
> + *    `acpi_amd_wbrf_remove_exclusion` to get other consumers notified.
> + */
> +bool acpi_amd_wbrf_supported_producer(struct device *dev);
> +int acpi_amd_wbrf_remove_exclusion(struct device *dev,
> +				   struct wbrf_ranges_in_out *in);
> +int acpi_amd_wbrf_add_exclusion(struct device *dev,
> +				struct wbrf_ranges_in_out *in);
> +
> +/*
> + * The expected flow for the consumers:
> + * 1) During probe, call `acpi_amd_wbrf_supported_consumer` to check if WBRF
> + *    can be enabled for the device.
> + * 2) Call `acpi_amd_wbrf_register_notifier` to register for notification
> + *    of frequency band change(add or remove) from other producers.
> + * 3) Call the `acpi_amd_wbrf_retrieve_exclusions` intentionally to retrieve
> + *    current active frequency bands considering some producers may broadcast
> + *    such information before the consumer is up.
> + * 4) On receiving a notification for frequency band change, run
> + *    `acpi_amd_wbrf_retrieve_exclusions` again to retrieve the latest
> + *    active frequency bands.
> + * 5) During driver cleanup, call `acpi_amd_wbrf_unregister_notifier` to
> + *    unregister the notifier.
> + */
> +bool acpi_amd_wbrf_supported_consumer(struct device *dev);
> +int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
> +				      struct wbrf_ranges_in_out *out);
> +int acpi_amd_wbrf_register_notifier(struct notifier_block *nb);
> +int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb);
> +#else
> +static inline
> +bool acpi_amd_wbrf_supported_consumer(struct device *dev)
> +{
> +	return false;
> +}
> +static inline
> +int acpi_amd_wbrf_remove_exclusion(struct device *dev,
> +				   struct wbrf_ranges_in_out *in)
> +{
> +	return -ENODEV;
> +}
> +static inline
> +int acpi_amd_wbrf_add_exclusion(struct device *dev,
> +				struct wbrf_ranges_in_out *in)
> +{
> +	return -ENODEV;
> +}
> +static inline
> +bool acpi_amd_wbrf_supported_producer(struct device *dev)
> +{
> +	return false;
> +}
> +static inline
> +int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
> +				      struct wbrf_ranges_in_out *out)
> +{
> +	return -ENODEV;
> +}
> +static inline
> +int acpi_amd_wbrf_register_notifier(struct notifier_block *nb)
> +{
> +	return -ENODEV;
> +}
> +static inline
> +int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb)
> +{
> +	return -ENODEV;
> +}
> +#endif
> +
> +#endif /* _ACPI_AMD_WBRF_H */


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [V11 1/8] ACPI: Add support for AMD ACPI based Wifi band RFI mitigation feature
  2023-08-31  6:20   ` Evan Quan
@ 2023-09-18 18:15     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 26+ messages in thread
From: Rafael J. Wysocki @ 2023-09-18 18:15 UTC (permalink / raw)
  To: Evan Quan
  Cc: lenb, johannes, davem, edumazet, kuba, pabeni, alexander.deucher,
	rafael, Lijo.Lazar, mario.limonciello, linux-kernel, linux-acpi,
	amd-gfx, dri-devel, linux-wireless, netdev

On Thu, Aug 31, 2023 at 8:21 AM Evan Quan <evan.quan@amd.com> 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.

The changelog is only marginally useful IMV.

It doesn't even mention the role of ACPI in all this, so it is quite
unclear what the patch is all about, why it does what it does and what
is actually done in it.

It is also unclear why this code is put into drivers/acpi/, which
should be explained.

> Signed-off-by: Evan Quan <evan.quan@amd.com>
> --
> v10->v11:
>   - fix typo(Simon)
> ---
>  drivers/acpi/Kconfig          |  17 ++
>  drivers/acpi/Makefile         |   2 +
>  drivers/acpi/amd_wbrf.c       | 414 ++++++++++++++++++++++++++++++++++
>  include/linux/acpi_amd_wbrf.h | 140 ++++++++++++
>  4 files changed, 573 insertions(+)
>  create mode 100644 drivers/acpi/amd_wbrf.c
>  create mode 100644 include/linux/acpi_amd_wbrf.h
>
> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> index 00dd309b6682..a092ea72d152 100644
> --- a/drivers/acpi/Kconfig
> +++ b/drivers/acpi/Kconfig
> @@ -594,6 +594,23 @@ config ACPI_PRMT
>           substantially increase computational overhead related to the
>           initialization of some server systems.
>
> +config WBRF_AMD_ACPI
> +       bool "ACPI based WBRF mechanism introduced by AMD"
> +       depends on ACPI
> +       default n
> +       help
> +         Wifi band RFI mitigation mechanism allows multiple drivers from
> +         different domains to notify the frequencies in use so that hardware
> +         can be reconfigured to avoid harmonic conflicts.

So drivers can notify the platform firmware IIUC, but that is not
really clear from the above.  I'm not even sure what the phrase
"notify the frequencies in use" is supposed to mean.

> +
> +         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.

No, this doesn't work.

Say you are a distro and you want to supply all of your users with the
same binary kernel image.  What are you expected to do to address the
above?

> +
>  endif  # ACPI
>
>  config X86_PM_TIMER
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index eaa09bf52f17..a3d2f259d0a5 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -132,3 +132,5 @@ obj-$(CONFIG_ARM64)         += arm64/
>  obj-$(CONFIG_ACPI_VIOT)                += viot.o
>
>  obj-$(CONFIG_RISCV)            += riscv/
> +
> +obj-$(CONFIG_WBRF_AMD_ACPI)    += amd_wbrf.o
> diff --git a/drivers/acpi/amd_wbrf.c b/drivers/acpi/amd_wbrf.c
> new file mode 100644
> index 000000000000..8ee0e2977a30
> --- /dev/null
> +++ b/drivers/acpi/amd_wbrf.c
> @@ -0,0 +1,414 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Wifi Band Exclusion Interface (AMD ACPI Implementation)
> + * Copyright (C) 2023 Advanced Micro Devices
> + *

Please document the code in this file at least basically.

You don't even explain what Wifi Band Exclusion means.

The OS-firmware interface that this code is based on should be
described here or a link to its description should be provided at the
very least.

As it is now, one needs to reverse engineer the patch in order to get
any idea about how this interface is designed.

> + */
> +
> +#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.
> + */

Without any additional information, the comment above is meaningless.

> +#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 naturally aligned.
> + * And unfortunately the design has been settled down.
> + */
> +struct amd_wbrf_ranges_out {
> +       u32                     num_of_ranges;
> +       struct exclusion_range  band_list[MAX_NUM_OF_WBRF_RANGES];
> +} __packed;
> +
> +static const guid_t wifi_acpi_dsm_guid =
> +       GUID_INIT(0x7b7656cf, 0xdc3d, 0x4c1c,
> +                 0x83, 0xe9, 0x66, 0xe7, 0x21, 0xde, 0x30, 0x70);
> +
> +static BLOCKING_NOTIFIER_HEAD(wbrf_chain_head);

I should have to reverse engineer the code in order to find out what
this notifier is for.

> +
> +static int wbrf_dsm(struct acpi_device *adev,
> +                   u8 fn,
> +                   union acpi_object *argv4)
> +{
> +       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_INTEGER:
> +               rc = obj->integer.value ? -EINVAL : 0;

rc = obj->integer.value;
if (rc)
        rc = -EINVAL;

And why -EINVAL?

> +               break;
> +       default:
> +               rc = -EOPNOTSUPP;

Why -EOPNOTSUPP?

> +       }
> +
> +       ACPI_FREE(obj);
> +
> +       return rc;
> +}
> +
> +static int wbrf_record(struct acpi_device *adev, uint8_t action,
> +                      struct wbrf_ranges_in_out *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++;
> +
> +       /*
> +        * The valid entry counter does not match with this told.
> +        * Something must went wrong.
> +        */

It would be better to say that the num_of_ranges value in the in
object supplied by the caller is required to be equal to the number of
entries in the band_list array in there.  And put that comment in
front of the loop above.

> +       if (num_of_ranges != in->num_of_ranges)
> +               return -EINVAL;
> +
> +       /*
> +        * Every input frequency band comes with two end points(start/end)
> +        * and each is accounted as an element. Meanwhile the range count
> +        * and action type are accounted as an element each.
> +        * So, the total element count = 2 * num_of_ranges + 1 + 1.

This information belongs to the (missing) description of the
OS-firmware interface used by this code.

> +        */
> +       num_of_elements = 2 * num_of_ranges + 1 + 1;

Why not 2 * num_of_ranges + 2?

> +
> +       tmp = kcalloc(num_of_elements, sizeof(*tmp), GFP_KERNEL);
> +       if (!tmp)
> +               return -ENOMEM;
> +
> +       argv4.package.type = ACPI_TYPE_PACKAGE;
> +       argv4.package.count = num_of_elements;
> +       argv4.package.elements = tmp;
> +
> +       tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
> +       tmp[arg_idx++].integer.value = num_of_ranges;
> +       tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
> +       tmp[arg_idx++].integer.value = action;

Why not use 0 and 1 as indices directly above and start arg_idx below at 2?

And why are 2 indices needed in the loop below?  What would be wrong
with using loop_idx alone and computing arg_idx from it?

> +
> +       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;
> +
> +               tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
> +               tmp[arg_idx++].integer.value = in->band_list[loop_idx].start;
> +               tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
> +               tmp[arg_idx++].integer.value = in->band_list[loop_idx].end;
> +       }
> +
> +       ret = wbrf_dsm(adev, WBRF_RECORD, &argv4);

So this is the only callers of wbrf_dsm() which is a super-simple
wrapper around acpi_evaluate_dsm().

Could it be folded in here?

> +
> +       kfree(tmp);
> +
> +       return ret;
> +}
> +
> +/**
> + * acpi_amd_wbrf_add_exclusion - broadcast the frequency band the device
> + *                               is using

Would it be possible to fit the above into one line of code?

And what does "exclusion" mean here?

Moreover, is the acpi_ prefix really useful?

> + *
> + * @dev: device pointer

What device does it point to?

> + * @in: input structure containing the frequency band the device is using
> + *
> + * Broadcast to other consumers the frequency band the device starts
> + * to use. Underneath the surface the information is cached into an
> + * internal buffer first. Then a notification is sent to all those
> + * registered consumers. So then they can retrieve that buffer to
> + * know the latest active frequency bands. The benefit with such design
> + * is for those consumers which have not been registered yet, they can
> + * still have a chance to retrieve such information later.

This is part of the design description missing from the preamble comment.

Besides, what really happens in this function is that it invokes the
firmware-provided _DSM to do something and then it calls the
wbrf_chain_head notifier chain in order to tell the other users of
this interface about that action.

And it isn't really clear what that action is, because of the missing
OS-firmware interface description.

> + */
> +int acpi_amd_wbrf_add_exclusion(struct device *dev,
> +                               struct wbrf_ranges_in_out *in)
> +{
> +       struct acpi_device *adev = ACPI_COMPANION(dev);
> +       int ret;
> +
> +       if (!adev)
> +               return -ENODEV;
> +
> +       ret = wbrf_record(adev, WBRF_RECORD_ADD, in);
> +       if (ret)
> +               return ret;
> +
> +       blocking_notifier_call_chain(&wbrf_chain_head,
> +                                    WBRF_CHANGED,
> +                                    NULL);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_add_exclusion);
> +
> +/**
> + * acpi_amd_wbrf_remove_exclusion - broadcast the frequency band the device
> + *                                  is no longer using
> + *
> + * @dev: device pointer
> + * @in: input structure containing the frequency band which is not used
> + *      by the device any more
> + *
> + * Broadcast to other consumers the frequency band the device stops
> + * to use. The stored information paired with this will be dropped
> + * from the internal buffer. And then a notification is sent to
> + * all registered consumers.
> + */
> +int acpi_amd_wbrf_remove_exclusion(struct device *dev,
> +                                  struct wbrf_ranges_in_out *in)
> +{
> +       struct acpi_device *adev = ACPI_COMPANION(dev);
> +       int ret;
> +
> +       if (!adev)
> +               return -ENODEV;
> +
> +       ret = wbrf_record(adev, WBRF_RECORD_REMOVE, in);
> +       if (ret)
> +               return ret;
> +
> +       blocking_notifier_call_chain(&wbrf_chain_head,
> +                                    WBRF_CHANGED,
> +                                    NULL);

Can you possibly reduce code duplication between this and the previous
function by combining them into one with an extra "action" argument?

> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_remove_exclusion);
> +
> +static bool acpi_amd_wbrf_supported_system(void)
> +{
> +       acpi_status status;
> +       acpi_handle handle;
> +
> +       status = acpi_get_handle(NULL, ACPI_AMD_WBRF_METHOD, &handle);
> +
> +       return ACPI_SUCCESS(status);
> +}
> +
> +/**
> + * acpi_amd_wbrf_supported_producer - determine if the WBRF can be enabled
> + *                                    for the device as a producer
> + *
> + * @dev: device pointer
> + *
> + * Determine if the platform equipped with necessary implementations to

How does it determine that?

> + * support WBRF for the device as a producer.
> + */
> +bool acpi_amd_wbrf_supported_producer(struct device *dev)
> +{
> +       struct acpi_device *adev = ACPI_COMPANION(dev);
> +
> +       if (!acpi_amd_wbrf_supported_system())
> +               return false;
> +
> +       if (!adev)
> +               return false;
> +
> +       return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid,
> +                             WBRF_REVISION,
> +                             BIT(WBRF_RECORD));

The WBRF_RECORD _DSM function support is checked here, but what if
WBRF_RETRIEVE is not supported?

> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_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);

The WBRF method needs to be described somewhere.

> +       if (ACPI_FAILURE(ret))
> +               return NULL;
> +
> +       return buf.pointer;
> +}
> +
> +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;
> +
> +       ACPI_FREE(obj);
> +
> +       return mask & BIT(WBRF_ENABLED) && (mask & funcs) == funcs;
> +}
> +
> +/**
> + * acpi_amd_wbrf_supported_consumer - determine if the WBRF can be enabled
> + *                                    for the device as a consumer
> + *
> + * @dev: device pointer
> + *
> + * Determine if the platform equipped with necessary implementations to
> + * support WBRF for the device as a consumer.

So when is "success" returned?

> + */
> +bool acpi_amd_wbrf_supported_consumer(struct device *dev)
> +{
> +       struct acpi_device *adev = ACPI_COMPANION(dev);
> +
> +       if (!acpi_amd_wbrf_supported_system())
> +               return false;
> +
> +       if (!adev)
> +               return false;
> +
> +       return check_acpi_wbrf(adev->handle,
> +                              WBRF_REVISION,
> +                              BIT(WBRF_RETRIEVE));
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_supported_consumer);
> +
> +/**
> + * acpi_amd_wbrf_retrieve_exclusions - retrieve current active frequency
> + *                                     bands
> + *
> + * @dev: device pointer
> + * @out: output structure containing all the active frequency bands
> + *
> + * Retrieve the current active frequency bands which were broadcasted
> + * by other producers. The consumer who calls this API should take
> + * proper actions if any of the frequency band may cause RFI with its
> + * own frequency band used.
> + */
> +int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
> +                                     struct wbrf_ranges_in_out *out)
> +{
> +       struct acpi_device *adev = ACPI_COMPANION(dev);
> +       struct amd_wbrf_ranges_out acpi_out = {0};
> +       union acpi_object *obj;
> +       int ret = 0;
> +
> +       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.

This again is part of the (missing) OS-firmware interface description.

> +        */
> +       if (obj->buffer.length > sizeof(acpi_out) ||
> +           obj->buffer.length < 4) {
> +               dev_err(dev, "Wrong sized WBRT information");
> +               ret = -EINVAL;
> +               goto out;
> +       }
> +       memcpy(&acpi_out, obj->buffer.pointer, obj->buffer.length);
> +
> +       out->num_of_ranges = acpi_out.num_of_ranges;
> +       memcpy(out->band_list, acpi_out.band_list, sizeof(acpi_out.band_list));

I've already asked about this memcpy() which IMO is questionable at least once.

The requirement that the binary format of data in out->band_list be
the same as the binary format of data in the buffer returned from the
platform firmware is at best artificial IMO.

> +
> +out:
> +       ACPI_FREE(obj);
> +
> +       return ret;
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_retrieve_exclusions);
> +
> +/**
> + * acpi_amd_wbrf_register_notifier - register for notifications of frequency
> + *                                   band update
> + *
> + * @nb: driver notifier block
> + *
> + * The consumer should register itself via this API. So that it can get
> + * notified timely on the frequency band updates from other producers.
> + */
> +int acpi_amd_wbrf_register_notifier(struct notifier_block *nb)
> +{
> +       return blocking_notifier_chain_register(&wbrf_chain_head, nb);
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_register_notifier);
> +
> +/**
> + * acpi_amd_wbrf_unregister_notifier - unregister for notifications of
> + *                                     frequency band update
> + *
> + * @nb: driver notifier block
> + *
> + * The consumer should call this API when it is longer interested with
> + * the frequency band updates from other producers. Usually, this should
> + * be performed during driver cleanup.
> + */
> +int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb)
> +{
> +       return blocking_notifier_chain_unregister(&wbrf_chain_head, nb);
> +}

The notifier change needs to be documented.  So far, it is not clear
who should register to it and why and how they are supposed to respond
to the notifications.

> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_unregister_notifier);
> diff --git a/include/linux/acpi_amd_wbrf.h b/include/linux/acpi_amd_wbrf.h
> new file mode 100644
> index 000000000000..c2363d664641
> --- /dev/null
> +++ b/include/linux/acpi_amd_wbrf.h
> @@ -0,0 +1,140 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Wifi Band Exclusion Interface (AMD ACPI Implementation)
> + * Copyright (C) 2023 Advanced Micro Devices
> + *
> + * 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.

And presumably, that interference causes throughput reduction or similar.

> + *
> + * To mitigate this, AMD has introduced an ACPI based mechanism to support
> + * WBRF(Wifi Band RFI mitigation Feature) for platforms with AMD dGPU + WLAN.
> + * This needs support from BIOS equipped with necessary AML implementations
> + * and dGPU firmwares.
> + *
> + * Some general terms:
> + * Producer: such component who can produce high-powered radio frequency

"such" is unnecessary here and below and I would use "that" instead of "who".

Maybe "Producer: Component that can generate high-frequency radio signal"?

> + * Consumer: such component who can adjust its in-use frequency in
> + *           response to the radio frequencies of other components to
> + *           mitigate the possible RFI.

"Consumer: Component that can adjust its radio frequency usage to
avoid RFI if notified by other components about their RF usage."

> + *
> + * To make the mechanism function, those producers should notify active use
> + * of their particular frequencies so that other consumers can make relative
> + * internal adjustments as necessary to avoid this resonance.

I would say "destructive interference" or similar.

> + */
> +
> +#ifndef _ACPI_AMD_WBRF_H
> +#define _ACPI_AMD_WBRF_H
> +
> +#include <linux/device.h>
> +#include <linux/notifier.h>
> +
> +/*
> + * A wbrf range is defined as a frequency band with start and end
> + * frequency point specified(in Hz). And a vaild range should have
> + * its start and end frequency point filled with non-zero values.
> + * Meanwhile, the maximum number of wbrf ranges is limited as
> + * `MAX_NUM_OF_WBRF_RANGES`.

I suppose that this means the maximum number of ranges that can be
used simultaneously in one operation or in one go?

> + */
> +#define MAX_NUM_OF_WBRF_RANGES         11
> +
> +struct exclusion_range {
> +       u64             start;
> +       u64             end;
> +};
> +
> +struct wbrf_ranges_in_out {
> +       u64                     num_of_ranges;
> +       struct exclusion_range  band_list[MAX_NUM_OF_WBRF_RANGES];
> +};
> +
> +/*
> + * The notification types for the consumers are defined as below.
> + * The consumers may need to take different actions in response to
> + * different notifications.

The above doesn't make sense to me.  There is only one value defined
below and the action to be carried out upon it is documented below.

Also this means that "WBRF consumers" are expected to register to the
notifier chain so as to get the notifications telling them to update
the list of the "active frequency bands" (whatever the last phrase
means).

> + * WBRF_CHANGED: there was some frequency band updates. The consumers
> + *               should retrieve the latest active frequency bands.
> + */
> +enum wbrf_notifier_actions {
> +       WBRF_CHANGED,
> +};
> +
> +#if IS_ENABLED(CONFIG_WBRF_AMD_ACPI)
> +/*
> + * The expected flow for the producers:
> + * 1) During probe, call `acpi_amd_wbrf_supported_producer` to check
> + *    if WBRF can be enabled for the device.

The "During probe" part is redundant here and below.  It obviously
should be done prior to using any RF, but not necessarily during probe
AFAICS.

> + * 2) On using some frequency band, call `acpi_amd_wbrf_add_exclusion`
> + *    to get other consumers properly notified.

What does "other" mean here?

Moreover, this isn't just about consumers IIUC, because the platform
firmware is involved too.

> + * 3) Or on stopping using some frequency band, call
> + *    `acpi_amd_wbrf_remove_exclusion` to get other consumers notified.
> + */
> +bool acpi_amd_wbrf_supported_producer(struct device *dev);
> +int acpi_amd_wbrf_remove_exclusion(struct device *dev,
> +                                  struct wbrf_ranges_in_out *in);
> +int acpi_amd_wbrf_add_exclusion(struct device *dev,
> +                               struct wbrf_ranges_in_out *in);
> +
> +/*
> + * The expected flow for the consumers:
> + * 1) During probe, call `acpi_amd_wbrf_supported_consumer` to check if WBRF
> + *    can be enabled for the device.
> + * 2) Call `acpi_amd_wbrf_register_notifier` to register for notification
> + *    of frequency band change(add or remove) from other producers.

Again, what does "other" mean here?

> + * 3) Call the `acpi_amd_wbrf_retrieve_exclusions` intentionally to retrieve

"intentionally" is redundant.

> + *    current active frequency bands considering some producers may broadcast
> + *    such information before the consumer is up.

"current active frequency bands in case some producers have registered
them already".

> + * 4) On receiving a notification for frequency band change, run
> + *    `acpi_amd_wbrf_retrieve_exclusions` again to retrieve the latest
> + *    active frequency bands.

"the most recent list of active frequency bands".

> + * 5) During driver cleanup, call `acpi_amd_wbrf_unregister_notifier` to
> + *    unregister the notifier.

This completely misses the information regarding what the consumers
are expected to do with the list of "active frequency bands" after
retrieving it.  Presumably, they are expected to adjust their RF usage
accordingly, but what if the rate at which that list changes is too
high for them to react?  Is there any rate limit or similar?

Moreover, does this require any synchronization between different
producers, for example?  Or is the platform firmware interface
expected to synchronize them sufficiently?

I can easily imagine a few race condition scenarios with the current
code in this patch, but I'm not sure if they are really problematic.
In any case, it would be good to document the mutual exclusion
requirements here or the lack thereof if none (which should be
explained).

> + */
> +bool acpi_amd_wbrf_supported_consumer(struct device *dev);
> +int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
> +                                     struct wbrf_ranges_in_out *out);
> +int acpi_amd_wbrf_register_notifier(struct notifier_block *nb);
> +int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb);
> +#else
> +static inline
> +bool acpi_amd_wbrf_supported_consumer(struct device *dev)
> +{
> +       return false;
> +}
> +static inline
> +int acpi_amd_wbrf_remove_exclusion(struct device *dev,
> +                                  struct wbrf_ranges_in_out *in)
> +{
> +       return -ENODEV;
> +}
> +static inline
> +int acpi_amd_wbrf_add_exclusion(struct device *dev,
> +                               struct wbrf_ranges_in_out *in)
> +{
> +       return -ENODEV;
> +}
> +static inline
> +bool acpi_amd_wbrf_supported_producer(struct device *dev)
> +{
> +       return false;
> +}
> +static inline
> +int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
> +                                     struct wbrf_ranges_in_out *out)
> +{
> +       return -ENODEV;
> +}
> +static inline
> +int acpi_amd_wbrf_register_notifier(struct notifier_block *nb)
> +{
> +       return -ENODEV;
> +}
> +static inline
> +int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb)
> +{
> +       return -ENODEV;
> +}
> +#endif
> +
> +#endif /* _ACPI_AMD_WBRF_H */
> --

Overall, I'm not even sure if drivers/acpi/ is the most suitable place
for this code.

The fact that it is using an ACPI-based OS-firmware interface by
itself is not sufficient for that.  x86 platform drivers also use ACPI
in vendor-specific ways and that by itself doesn't mean that they
should be located in drivers/acpi/.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [V11 1/8] ACPI: Add support for AMD ACPI based Wifi band RFI mitigation feature
@ 2023-09-18 18:15     ` Rafael J. Wysocki
  0 siblings, 0 replies; 26+ messages in thread
From: Rafael J. Wysocki @ 2023-09-18 18:15 UTC (permalink / raw)
  To: Evan Quan
  Cc: rafael, netdev, dri-devel, Lijo.Lazar, linux-wireless,
	linux-kernel, amd-gfx, linux-acpi, edumazet, mario.limonciello,
	alexander.deucher, kuba, johannes, pabeni, davem, lenb

On Thu, Aug 31, 2023 at 8:21 AM Evan Quan <evan.quan@amd.com> 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.

The changelog is only marginally useful IMV.

It doesn't even mention the role of ACPI in all this, so it is quite
unclear what the patch is all about, why it does what it does and what
is actually done in it.

It is also unclear why this code is put into drivers/acpi/, which
should be explained.

> Signed-off-by: Evan Quan <evan.quan@amd.com>
> --
> v10->v11:
>   - fix typo(Simon)
> ---
>  drivers/acpi/Kconfig          |  17 ++
>  drivers/acpi/Makefile         |   2 +
>  drivers/acpi/amd_wbrf.c       | 414 ++++++++++++++++++++++++++++++++++
>  include/linux/acpi_amd_wbrf.h | 140 ++++++++++++
>  4 files changed, 573 insertions(+)
>  create mode 100644 drivers/acpi/amd_wbrf.c
>  create mode 100644 include/linux/acpi_amd_wbrf.h
>
> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> index 00dd309b6682..a092ea72d152 100644
> --- a/drivers/acpi/Kconfig
> +++ b/drivers/acpi/Kconfig
> @@ -594,6 +594,23 @@ config ACPI_PRMT
>           substantially increase computational overhead related to the
>           initialization of some server systems.
>
> +config WBRF_AMD_ACPI
> +       bool "ACPI based WBRF mechanism introduced by AMD"
> +       depends on ACPI
> +       default n
> +       help
> +         Wifi band RFI mitigation mechanism allows multiple drivers from
> +         different domains to notify the frequencies in use so that hardware
> +         can be reconfigured to avoid harmonic conflicts.

So drivers can notify the platform firmware IIUC, but that is not
really clear from the above.  I'm not even sure what the phrase
"notify the frequencies in use" is supposed to mean.

> +
> +         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.

No, this doesn't work.

Say you are a distro and you want to supply all of your users with the
same binary kernel image.  What are you expected to do to address the
above?

> +
>  endif  # ACPI
>
>  config X86_PM_TIMER
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index eaa09bf52f17..a3d2f259d0a5 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -132,3 +132,5 @@ obj-$(CONFIG_ARM64)         += arm64/
>  obj-$(CONFIG_ACPI_VIOT)                += viot.o
>
>  obj-$(CONFIG_RISCV)            += riscv/
> +
> +obj-$(CONFIG_WBRF_AMD_ACPI)    += amd_wbrf.o
> diff --git a/drivers/acpi/amd_wbrf.c b/drivers/acpi/amd_wbrf.c
> new file mode 100644
> index 000000000000..8ee0e2977a30
> --- /dev/null
> +++ b/drivers/acpi/amd_wbrf.c
> @@ -0,0 +1,414 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Wifi Band Exclusion Interface (AMD ACPI Implementation)
> + * Copyright (C) 2023 Advanced Micro Devices
> + *

Please document the code in this file at least basically.

You don't even explain what Wifi Band Exclusion means.

The OS-firmware interface that this code is based on should be
described here or a link to its description should be provided at the
very least.

As it is now, one needs to reverse engineer the patch in order to get
any idea about how this interface is designed.

> + */
> +
> +#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.
> + */

Without any additional information, the comment above is meaningless.

> +#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 naturally aligned.
> + * And unfortunately the design has been settled down.
> + */
> +struct amd_wbrf_ranges_out {
> +       u32                     num_of_ranges;
> +       struct exclusion_range  band_list[MAX_NUM_OF_WBRF_RANGES];
> +} __packed;
> +
> +static const guid_t wifi_acpi_dsm_guid =
> +       GUID_INIT(0x7b7656cf, 0xdc3d, 0x4c1c,
> +                 0x83, 0xe9, 0x66, 0xe7, 0x21, 0xde, 0x30, 0x70);
> +
> +static BLOCKING_NOTIFIER_HEAD(wbrf_chain_head);

I should have to reverse engineer the code in order to find out what
this notifier is for.

> +
> +static int wbrf_dsm(struct acpi_device *adev,
> +                   u8 fn,
> +                   union acpi_object *argv4)
> +{
> +       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_INTEGER:
> +               rc = obj->integer.value ? -EINVAL : 0;

rc = obj->integer.value;
if (rc)
        rc = -EINVAL;

And why -EINVAL?

> +               break;
> +       default:
> +               rc = -EOPNOTSUPP;

Why -EOPNOTSUPP?

> +       }
> +
> +       ACPI_FREE(obj);
> +
> +       return rc;
> +}
> +
> +static int wbrf_record(struct acpi_device *adev, uint8_t action,
> +                      struct wbrf_ranges_in_out *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++;
> +
> +       /*
> +        * The valid entry counter does not match with this told.
> +        * Something must went wrong.
> +        */

It would be better to say that the num_of_ranges value in the in
object supplied by the caller is required to be equal to the number of
entries in the band_list array in there.  And put that comment in
front of the loop above.

> +       if (num_of_ranges != in->num_of_ranges)
> +               return -EINVAL;
> +
> +       /*
> +        * Every input frequency band comes with two end points(start/end)
> +        * and each is accounted as an element. Meanwhile the range count
> +        * and action type are accounted as an element each.
> +        * So, the total element count = 2 * num_of_ranges + 1 + 1.

This information belongs to the (missing) description of the
OS-firmware interface used by this code.

> +        */
> +       num_of_elements = 2 * num_of_ranges + 1 + 1;

Why not 2 * num_of_ranges + 2?

> +
> +       tmp = kcalloc(num_of_elements, sizeof(*tmp), GFP_KERNEL);
> +       if (!tmp)
> +               return -ENOMEM;
> +
> +       argv4.package.type = ACPI_TYPE_PACKAGE;
> +       argv4.package.count = num_of_elements;
> +       argv4.package.elements = tmp;
> +
> +       tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
> +       tmp[arg_idx++].integer.value = num_of_ranges;
> +       tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
> +       tmp[arg_idx++].integer.value = action;

Why not use 0 and 1 as indices directly above and start arg_idx below at 2?

And why are 2 indices needed in the loop below?  What would be wrong
with using loop_idx alone and computing arg_idx from it?

> +
> +       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;
> +
> +               tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
> +               tmp[arg_idx++].integer.value = in->band_list[loop_idx].start;
> +               tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER;
> +               tmp[arg_idx++].integer.value = in->band_list[loop_idx].end;
> +       }
> +
> +       ret = wbrf_dsm(adev, WBRF_RECORD, &argv4);

So this is the only callers of wbrf_dsm() which is a super-simple
wrapper around acpi_evaluate_dsm().

Could it be folded in here?

> +
> +       kfree(tmp);
> +
> +       return ret;
> +}
> +
> +/**
> + * acpi_amd_wbrf_add_exclusion - broadcast the frequency band the device
> + *                               is using

Would it be possible to fit the above into one line of code?

And what does "exclusion" mean here?

Moreover, is the acpi_ prefix really useful?

> + *
> + * @dev: device pointer

What device does it point to?

> + * @in: input structure containing the frequency band the device is using
> + *
> + * Broadcast to other consumers the frequency band the device starts
> + * to use. Underneath the surface the information is cached into an
> + * internal buffer first. Then a notification is sent to all those
> + * registered consumers. So then they can retrieve that buffer to
> + * know the latest active frequency bands. The benefit with such design
> + * is for those consumers which have not been registered yet, they can
> + * still have a chance to retrieve such information later.

This is part of the design description missing from the preamble comment.

Besides, what really happens in this function is that it invokes the
firmware-provided _DSM to do something and then it calls the
wbrf_chain_head notifier chain in order to tell the other users of
this interface about that action.

And it isn't really clear what that action is, because of the missing
OS-firmware interface description.

> + */
> +int acpi_amd_wbrf_add_exclusion(struct device *dev,
> +                               struct wbrf_ranges_in_out *in)
> +{
> +       struct acpi_device *adev = ACPI_COMPANION(dev);
> +       int ret;
> +
> +       if (!adev)
> +               return -ENODEV;
> +
> +       ret = wbrf_record(adev, WBRF_RECORD_ADD, in);
> +       if (ret)
> +               return ret;
> +
> +       blocking_notifier_call_chain(&wbrf_chain_head,
> +                                    WBRF_CHANGED,
> +                                    NULL);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_add_exclusion);
> +
> +/**
> + * acpi_amd_wbrf_remove_exclusion - broadcast the frequency band the device
> + *                                  is no longer using
> + *
> + * @dev: device pointer
> + * @in: input structure containing the frequency band which is not used
> + *      by the device any more
> + *
> + * Broadcast to other consumers the frequency band the device stops
> + * to use. The stored information paired with this will be dropped
> + * from the internal buffer. And then a notification is sent to
> + * all registered consumers.
> + */
> +int acpi_amd_wbrf_remove_exclusion(struct device *dev,
> +                                  struct wbrf_ranges_in_out *in)
> +{
> +       struct acpi_device *adev = ACPI_COMPANION(dev);
> +       int ret;
> +
> +       if (!adev)
> +               return -ENODEV;
> +
> +       ret = wbrf_record(adev, WBRF_RECORD_REMOVE, in);
> +       if (ret)
> +               return ret;
> +
> +       blocking_notifier_call_chain(&wbrf_chain_head,
> +                                    WBRF_CHANGED,
> +                                    NULL);

Can you possibly reduce code duplication between this and the previous
function by combining them into one with an extra "action" argument?

> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_remove_exclusion);
> +
> +static bool acpi_amd_wbrf_supported_system(void)
> +{
> +       acpi_status status;
> +       acpi_handle handle;
> +
> +       status = acpi_get_handle(NULL, ACPI_AMD_WBRF_METHOD, &handle);
> +
> +       return ACPI_SUCCESS(status);
> +}
> +
> +/**
> + * acpi_amd_wbrf_supported_producer - determine if the WBRF can be enabled
> + *                                    for the device as a producer
> + *
> + * @dev: device pointer
> + *
> + * Determine if the platform equipped with necessary implementations to

How does it determine that?

> + * support WBRF for the device as a producer.
> + */
> +bool acpi_amd_wbrf_supported_producer(struct device *dev)
> +{
> +       struct acpi_device *adev = ACPI_COMPANION(dev);
> +
> +       if (!acpi_amd_wbrf_supported_system())
> +               return false;
> +
> +       if (!adev)
> +               return false;
> +
> +       return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid,
> +                             WBRF_REVISION,
> +                             BIT(WBRF_RECORD));

The WBRF_RECORD _DSM function support is checked here, but what if
WBRF_RETRIEVE is not supported?

> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_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);

The WBRF method needs to be described somewhere.

> +       if (ACPI_FAILURE(ret))
> +               return NULL;
> +
> +       return buf.pointer;
> +}
> +
> +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;
> +
> +       ACPI_FREE(obj);
> +
> +       return mask & BIT(WBRF_ENABLED) && (mask & funcs) == funcs;
> +}
> +
> +/**
> + * acpi_amd_wbrf_supported_consumer - determine if the WBRF can be enabled
> + *                                    for the device as a consumer
> + *
> + * @dev: device pointer
> + *
> + * Determine if the platform equipped with necessary implementations to
> + * support WBRF for the device as a consumer.

So when is "success" returned?

> + */
> +bool acpi_amd_wbrf_supported_consumer(struct device *dev)
> +{
> +       struct acpi_device *adev = ACPI_COMPANION(dev);
> +
> +       if (!acpi_amd_wbrf_supported_system())
> +               return false;
> +
> +       if (!adev)
> +               return false;
> +
> +       return check_acpi_wbrf(adev->handle,
> +                              WBRF_REVISION,
> +                              BIT(WBRF_RETRIEVE));
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_supported_consumer);
> +
> +/**
> + * acpi_amd_wbrf_retrieve_exclusions - retrieve current active frequency
> + *                                     bands
> + *
> + * @dev: device pointer
> + * @out: output structure containing all the active frequency bands
> + *
> + * Retrieve the current active frequency bands which were broadcasted
> + * by other producers. The consumer who calls this API should take
> + * proper actions if any of the frequency band may cause RFI with its
> + * own frequency band used.
> + */
> +int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
> +                                     struct wbrf_ranges_in_out *out)
> +{
> +       struct acpi_device *adev = ACPI_COMPANION(dev);
> +       struct amd_wbrf_ranges_out acpi_out = {0};
> +       union acpi_object *obj;
> +       int ret = 0;
> +
> +       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.

This again is part of the (missing) OS-firmware interface description.

> +        */
> +       if (obj->buffer.length > sizeof(acpi_out) ||
> +           obj->buffer.length < 4) {
> +               dev_err(dev, "Wrong sized WBRT information");
> +               ret = -EINVAL;
> +               goto out;
> +       }
> +       memcpy(&acpi_out, obj->buffer.pointer, obj->buffer.length);
> +
> +       out->num_of_ranges = acpi_out.num_of_ranges;
> +       memcpy(out->band_list, acpi_out.band_list, sizeof(acpi_out.band_list));

I've already asked about this memcpy() which IMO is questionable at least once.

The requirement that the binary format of data in out->band_list be
the same as the binary format of data in the buffer returned from the
platform firmware is at best artificial IMO.

> +
> +out:
> +       ACPI_FREE(obj);
> +
> +       return ret;
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_retrieve_exclusions);
> +
> +/**
> + * acpi_amd_wbrf_register_notifier - register for notifications of frequency
> + *                                   band update
> + *
> + * @nb: driver notifier block
> + *
> + * The consumer should register itself via this API. So that it can get
> + * notified timely on the frequency band updates from other producers.
> + */
> +int acpi_amd_wbrf_register_notifier(struct notifier_block *nb)
> +{
> +       return blocking_notifier_chain_register(&wbrf_chain_head, nb);
> +}
> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_register_notifier);
> +
> +/**
> + * acpi_amd_wbrf_unregister_notifier - unregister for notifications of
> + *                                     frequency band update
> + *
> + * @nb: driver notifier block
> + *
> + * The consumer should call this API when it is longer interested with
> + * the frequency band updates from other producers. Usually, this should
> + * be performed during driver cleanup.
> + */
> +int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb)
> +{
> +       return blocking_notifier_chain_unregister(&wbrf_chain_head, nb);
> +}

The notifier change needs to be documented.  So far, it is not clear
who should register to it and why and how they are supposed to respond
to the notifications.

> +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_unregister_notifier);
> diff --git a/include/linux/acpi_amd_wbrf.h b/include/linux/acpi_amd_wbrf.h
> new file mode 100644
> index 000000000000..c2363d664641
> --- /dev/null
> +++ b/include/linux/acpi_amd_wbrf.h
> @@ -0,0 +1,140 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Wifi Band Exclusion Interface (AMD ACPI Implementation)
> + * Copyright (C) 2023 Advanced Micro Devices
> + *
> + * 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.

And presumably, that interference causes throughput reduction or similar.

> + *
> + * To mitigate this, AMD has introduced an ACPI based mechanism to support
> + * WBRF(Wifi Band RFI mitigation Feature) for platforms with AMD dGPU + WLAN.
> + * This needs support from BIOS equipped with necessary AML implementations
> + * and dGPU firmwares.
> + *
> + * Some general terms:
> + * Producer: such component who can produce high-powered radio frequency

"such" is unnecessary here and below and I would use "that" instead of "who".

Maybe "Producer: Component that can generate high-frequency radio signal"?

> + * Consumer: such component who can adjust its in-use frequency in
> + *           response to the radio frequencies of other components to
> + *           mitigate the possible RFI.

"Consumer: Component that can adjust its radio frequency usage to
avoid RFI if notified by other components about their RF usage."

> + *
> + * To make the mechanism function, those producers should notify active use
> + * of their particular frequencies so that other consumers can make relative
> + * internal adjustments as necessary to avoid this resonance.

I would say "destructive interference" or similar.

> + */
> +
> +#ifndef _ACPI_AMD_WBRF_H
> +#define _ACPI_AMD_WBRF_H
> +
> +#include <linux/device.h>
> +#include <linux/notifier.h>
> +
> +/*
> + * A wbrf range is defined as a frequency band with start and end
> + * frequency point specified(in Hz). And a vaild range should have
> + * its start and end frequency point filled with non-zero values.
> + * Meanwhile, the maximum number of wbrf ranges is limited as
> + * `MAX_NUM_OF_WBRF_RANGES`.

I suppose that this means the maximum number of ranges that can be
used simultaneously in one operation or in one go?

> + */
> +#define MAX_NUM_OF_WBRF_RANGES         11
> +
> +struct exclusion_range {
> +       u64             start;
> +       u64             end;
> +};
> +
> +struct wbrf_ranges_in_out {
> +       u64                     num_of_ranges;
> +       struct exclusion_range  band_list[MAX_NUM_OF_WBRF_RANGES];
> +};
> +
> +/*
> + * The notification types for the consumers are defined as below.
> + * The consumers may need to take different actions in response to
> + * different notifications.

The above doesn't make sense to me.  There is only one value defined
below and the action to be carried out upon it is documented below.

Also this means that "WBRF consumers" are expected to register to the
notifier chain so as to get the notifications telling them to update
the list of the "active frequency bands" (whatever the last phrase
means).

> + * WBRF_CHANGED: there was some frequency band updates. The consumers
> + *               should retrieve the latest active frequency bands.
> + */
> +enum wbrf_notifier_actions {
> +       WBRF_CHANGED,
> +};
> +
> +#if IS_ENABLED(CONFIG_WBRF_AMD_ACPI)
> +/*
> + * The expected flow for the producers:
> + * 1) During probe, call `acpi_amd_wbrf_supported_producer` to check
> + *    if WBRF can be enabled for the device.

The "During probe" part is redundant here and below.  It obviously
should be done prior to using any RF, but not necessarily during probe
AFAICS.

> + * 2) On using some frequency band, call `acpi_amd_wbrf_add_exclusion`
> + *    to get other consumers properly notified.

What does "other" mean here?

Moreover, this isn't just about consumers IIUC, because the platform
firmware is involved too.

> + * 3) Or on stopping using some frequency band, call
> + *    `acpi_amd_wbrf_remove_exclusion` to get other consumers notified.
> + */
> +bool acpi_amd_wbrf_supported_producer(struct device *dev);
> +int acpi_amd_wbrf_remove_exclusion(struct device *dev,
> +                                  struct wbrf_ranges_in_out *in);
> +int acpi_amd_wbrf_add_exclusion(struct device *dev,
> +                               struct wbrf_ranges_in_out *in);
> +
> +/*
> + * The expected flow for the consumers:
> + * 1) During probe, call `acpi_amd_wbrf_supported_consumer` to check if WBRF
> + *    can be enabled for the device.
> + * 2) Call `acpi_amd_wbrf_register_notifier` to register for notification
> + *    of frequency band change(add or remove) from other producers.

Again, what does "other" mean here?

> + * 3) Call the `acpi_amd_wbrf_retrieve_exclusions` intentionally to retrieve

"intentionally" is redundant.

> + *    current active frequency bands considering some producers may broadcast
> + *    such information before the consumer is up.

"current active frequency bands in case some producers have registered
them already".

> + * 4) On receiving a notification for frequency band change, run
> + *    `acpi_amd_wbrf_retrieve_exclusions` again to retrieve the latest
> + *    active frequency bands.

"the most recent list of active frequency bands".

> + * 5) During driver cleanup, call `acpi_amd_wbrf_unregister_notifier` to
> + *    unregister the notifier.

This completely misses the information regarding what the consumers
are expected to do with the list of "active frequency bands" after
retrieving it.  Presumably, they are expected to adjust their RF usage
accordingly, but what if the rate at which that list changes is too
high for them to react?  Is there any rate limit or similar?

Moreover, does this require any synchronization between different
producers, for example?  Or is the platform firmware interface
expected to synchronize them sufficiently?

I can easily imagine a few race condition scenarios with the current
code in this patch, but I'm not sure if they are really problematic.
In any case, it would be good to document the mutual exclusion
requirements here or the lack thereof if none (which should be
explained).

> + */
> +bool acpi_amd_wbrf_supported_consumer(struct device *dev);
> +int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
> +                                     struct wbrf_ranges_in_out *out);
> +int acpi_amd_wbrf_register_notifier(struct notifier_block *nb);
> +int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb);
> +#else
> +static inline
> +bool acpi_amd_wbrf_supported_consumer(struct device *dev)
> +{
> +       return false;
> +}
> +static inline
> +int acpi_amd_wbrf_remove_exclusion(struct device *dev,
> +                                  struct wbrf_ranges_in_out *in)
> +{
> +       return -ENODEV;
> +}
> +static inline
> +int acpi_amd_wbrf_add_exclusion(struct device *dev,
> +                               struct wbrf_ranges_in_out *in)
> +{
> +       return -ENODEV;
> +}
> +static inline
> +bool acpi_amd_wbrf_supported_producer(struct device *dev)
> +{
> +       return false;
> +}
> +static inline
> +int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
> +                                     struct wbrf_ranges_in_out *out)
> +{
> +       return -ENODEV;
> +}
> +static inline
> +int acpi_amd_wbrf_register_notifier(struct notifier_block *nb)
> +{
> +       return -ENODEV;
> +}
> +static inline
> +int acpi_amd_wbrf_unregister_notifier(struct notifier_block *nb)
> +{
> +       return -ENODEV;
> +}
> +#endif
> +
> +#endif /* _ACPI_AMD_WBRF_H */
> --

Overall, I'm not even sure if drivers/acpi/ is the most suitable place
for this code.

The fact that it is using an ACPI-based OS-firmware interface by
itself is not sufficient for that.  x86 platform drivers also use ACPI
in vendor-specific ways and that by itself doesn't mean that they
should be located in drivers/acpi/.

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2023-09-18 18:16 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-31  6:20 [V11 0/8] Enable Wifi RFI interference mitigation feature support Evan Quan
2023-08-31  6:20 ` Evan Quan
2023-08-31  6:20 ` [V11 1/8] ACPI: Add support for AMD ACPI based Wifi band RFI mitigation feature Evan Quan
2023-08-31  6:20   ` Evan Quan
2023-09-18 13:04   ` Mario Limonciello
2023-09-18 13:04     ` Mario Limonciello
2023-09-18 18:15   ` Rafael J. Wysocki
2023-09-18 18:15     ` Rafael J. Wysocki
2023-08-31  6:20 ` [V11 2/8] cfg80211: expose nl80211_chan_width_to_mhz for wide sharing Evan Quan
2023-08-31  6:20   ` Evan Quan
2023-08-31  6:20 ` [V11 3/8] wifi: mac80211: Add support for WBRF features Evan Quan
2023-08-31  6:20   ` Evan Quan
2023-09-01 14:32   ` Jeff Johnson
2023-09-01 14:32     ` Jeff Johnson
2023-09-06 19:57     ` Mario Limonciello
2023-09-06 19:57       ` Mario Limonciello
2023-08-31  6:20 ` [V11 4/8] drm/amd/pm: update driver_if and ppsmc headers for coming wbrf feature Evan Quan
2023-08-31  6:20   ` Evan Quan
2023-08-31  6:20 ` [V11 5/8] drm/amd/pm: setup the framework to support Wifi RFI mitigation feature Evan Quan
2023-08-31  6:20   ` Evan Quan
2023-08-31  6:20 ` [V11 6/8] drm/amd/pm: add flood detection for wbrf events Evan Quan
2023-08-31  6:20   ` Evan Quan
2023-08-31  6:20 ` [V11 7/8] drm/amd/pm: enable Wifi RFI mitigation feature support for SMU13.0.0 Evan Quan
2023-08-31  6:20   ` Evan Quan
2023-08-31  6:20 ` [V11 8/8] drm/amd/pm: enable Wifi RFI mitigation feature support for SMU13.0.7 Evan Quan
2023-08-31  6:20   ` Evan Quan

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.